@hyperframes/producer 0.8.198 → 0.8.200
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 +176 -166
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +65 -62
- package/dist/index.js +756 -451
- package/dist/public-server.js +566 -289
- package/dist/renderRequest.d.ts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/services/distributed/chunkWeights.d.ts +2 -0
- package/dist/services/hdrCompositor.d.ts +5 -5
- package/dist/services/hdrFrameWindows.d.ts +23 -0
- package/dist/services/htmlCompiler.d.ts +0 -2
- package/dist/services/render/audioPadTrim.d.ts +2 -0
- package/dist/services/render/renderEtaGuard.d.ts +35 -0
- package/dist/services/render/stages/captureHdrResources.d.ts +5 -17
- package/dist/services/render/stages/compileStage.d.ts +1 -1
- package/dist/services/render/stages/freezePlan.d.ts +1 -0
- package/dist/services/renderOrchestrator.d.ts +7 -6
- package/package.json +7 -7
package/dist/distributed.js
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
cpSync as cpSync2,
|
|
6
6
|
existsSync as existsSync15,
|
|
7
7
|
linkSync,
|
|
8
|
-
mkdirSync as
|
|
9
|
-
rmSync as
|
|
8
|
+
mkdirSync as mkdirSync9,
|
|
9
|
+
rmSync as rmSync8,
|
|
10
10
|
writeFileSync as writeFileSync6
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { extname as extname4, isAbsolute as isAbsolute3, join as join21, relative as relative5, sep as sep2 } from "node:path";
|
|
@@ -66,10 +66,10 @@ var defaultLogger = createConsoleLogger("info");
|
|
|
66
66
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
67
67
|
import {
|
|
68
68
|
existsSync as existsSync10,
|
|
69
|
-
mkdirSync as
|
|
70
|
-
readFileSync as
|
|
69
|
+
mkdirSync as mkdirSync5,
|
|
70
|
+
readFileSync as readFileSync5,
|
|
71
71
|
readdirSync as readdirSync3,
|
|
72
|
-
rmSync as
|
|
72
|
+
rmSync as rmSync6,
|
|
73
73
|
statSync as statSync4,
|
|
74
74
|
writeFileSync as writeFileSync3,
|
|
75
75
|
copyFileSync as copyFileSync4,
|
|
@@ -1297,17 +1297,6 @@ import {
|
|
|
1297
1297
|
} from "@hyperframes/engine";
|
|
1298
1298
|
|
|
1299
1299
|
// src/services/render/stages/captureHdrResources.ts
|
|
1300
|
-
import {
|
|
1301
|
-
closeSync as closeSync2,
|
|
1302
|
-
constants as constants2,
|
|
1303
|
-
fstatSync as fstatSync2,
|
|
1304
|
-
mkdirSync as mkdirSync2,
|
|
1305
|
-
mkdtempSync,
|
|
1306
|
-
openSync as openSync2,
|
|
1307
|
-
readFileSync as readFileSync2,
|
|
1308
|
-
rmSync as rmSync2,
|
|
1309
|
-
statfsSync
|
|
1310
|
-
} from "node:fs";
|
|
1311
1300
|
import {
|
|
1312
1301
|
decodePngToRgb48le,
|
|
1313
1302
|
extractMediaMetadata as extractMediaMetadata2,
|
|
@@ -1321,8 +1310,8 @@ import {
|
|
|
1321
1310
|
runFfmpeg
|
|
1322
1311
|
} from "@hyperframes/engine";
|
|
1323
1312
|
import { fpsToFfmpegArg, fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
|
|
1324
|
-
var NO_FOLLOW_FLAG = constants2.O_NOFOLLOW ?? 0;
|
|
1325
1313
|
var BYTES_PER_MIB = 1024 * 1024;
|
|
1314
|
+
var DEFAULT_HDR_WINDOW_BYTES = 512 * BYTES_PER_MIB;
|
|
1326
1315
|
|
|
1327
1316
|
// src/services/render/stages/captureStreamingStage.ts
|
|
1328
1317
|
import {
|
|
@@ -1476,7 +1465,7 @@ import { suggestMatchingPreset } from "@hyperframes/core";
|
|
|
1476
1465
|
// src/services/htmlCompiler.ts
|
|
1477
1466
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
1478
1467
|
import { createHash as createHash3 } from "node:crypto";
|
|
1479
|
-
import { createReadStream as createReadStream2, existsSync as existsSync6, mkdirSync as
|
|
1468
|
+
import { createReadStream as createReadStream2, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3 } from "fs";
|
|
1480
1469
|
import { join as join5, dirname as dirname4, resolve as resolve5, basename as basename3, relative as relative2 } from "path";
|
|
1481
1470
|
import { parseHTML as parseHTML4 } from "linkedom";
|
|
1482
1471
|
import {
|
|
@@ -1704,7 +1693,7 @@ import {
|
|
|
1704
1693
|
|
|
1705
1694
|
// src/services/animatedGifPrep.ts
|
|
1706
1695
|
import { createHash as createHash2 } from "node:crypto";
|
|
1707
|
-
import { copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as
|
|
1696
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2 } from "node:fs";
|
|
1708
1697
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join4, resolve as resolve4 } from "node:path";
|
|
1709
1698
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
1710
1699
|
import { parseAnimatedGifMetadata } from "@hyperframes/core";
|
|
@@ -1862,10 +1851,10 @@ async function runAnimatedGifTranscode(request) {
|
|
|
1862
1851
|
}
|
|
1863
1852
|
async function ensurePreparedWebm(input) {
|
|
1864
1853
|
if (!existsSync4(dirname3(input.cachePath))) {
|
|
1865
|
-
|
|
1854
|
+
mkdirSync2(dirname3(input.cachePath), { recursive: true });
|
|
1866
1855
|
}
|
|
1867
1856
|
if (!existsSync4(dirname3(input.outputPath))) {
|
|
1868
|
-
|
|
1857
|
+
mkdirSync2(dirname3(input.outputPath), { recursive: true });
|
|
1869
1858
|
}
|
|
1870
1859
|
if (!isUsableFile(input.cachePath)) {
|
|
1871
1860
|
const tmpPath = `${input.cachePath}.tmp-${process.pid}-${Date.now()}`;
|
|
@@ -1889,10 +1878,10 @@ async function ensurePreparedWebm(input) {
|
|
|
1889
1878
|
if (!isUsableFile(input.cachePath)) {
|
|
1890
1879
|
renameWithRetry3(tmpPath, input.cachePath);
|
|
1891
1880
|
} else {
|
|
1892
|
-
|
|
1881
|
+
rmSync2(tmpPath, { force: true });
|
|
1893
1882
|
}
|
|
1894
1883
|
} catch (error) {
|
|
1895
|
-
|
|
1884
|
+
rmSync2(tmpPath, { force: true });
|
|
1896
1885
|
throw error;
|
|
1897
1886
|
}
|
|
1898
1887
|
}
|
|
@@ -1962,7 +1951,7 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
1962
1951
|
if (!hasGifExtension(src)) continue;
|
|
1963
1952
|
const sourcePath = resolveGifSourcePath(src, options);
|
|
1964
1953
|
if (!sourcePath) continue;
|
|
1965
|
-
const bytes =
|
|
1954
|
+
const bytes = readFileSync2(sourcePath);
|
|
1966
1955
|
const metadata = parseAnimatedGifMetadata(bytes);
|
|
1967
1956
|
if (!metadata?.animated) continue;
|
|
1968
1957
|
const loop = resolveLoop(metadata, readLoopOverride(img));
|
|
@@ -2322,10 +2311,6 @@ var THREE_D_CONTEXT_PATTERN = /transform-style\s*:\s*preserve-3d|backface-visibi
|
|
|
2322
2311
|
function detectThreeDTransformUsage(html) {
|
|
2323
2312
|
return THREE_D_CONTEXT_PATTERN.test(html);
|
|
2324
2313
|
}
|
|
2325
|
-
var MIX_BLEND_MODE_PATTERN = /mix-blend-mode\s*:/i;
|
|
2326
|
-
function detectMixBlendModeUsage(html) {
|
|
2327
|
-
return MIX_BLEND_MODE_PATTERN.test(html);
|
|
2328
|
-
}
|
|
2329
2314
|
var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
|
|
2330
2315
|
function detectAncestorBackgroundImage(html) {
|
|
2331
2316
|
const { document: document2 } = parseHTML4(html);
|
|
@@ -2375,7 +2360,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
2375
2360
|
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
2376
2361
|
let filePath = src;
|
|
2377
2362
|
if (isHttpUrl(src)) {
|
|
2378
|
-
if (!existsSync6(downloadDir))
|
|
2363
|
+
if (!existsSync6(downloadDir)) mkdirSync3(downloadDir, { recursive: true });
|
|
2379
2364
|
try {
|
|
2380
2365
|
filePath = await downloadToTemp(src, downloadDir, void 0, void 0, void 0, {
|
|
2381
2366
|
onTelemetry: logRemoteDownloadTelemetry
|
|
@@ -2958,7 +2943,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
|
|
|
2958
2943
|
var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
2959
2944
|
async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
|
|
2960
2945
|
if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
|
|
2961
|
-
if (!existsSync6(remoteDir))
|
|
2946
|
+
if (!existsSync6(remoteDir)) mkdirSync3(remoteDir, { recursive: true });
|
|
2962
2947
|
const urlToLocal = /* @__PURE__ */ new Map();
|
|
2963
2948
|
await Promise.all(
|
|
2964
2949
|
[...urlSet].map(async (url) => {
|
|
@@ -3304,7 +3289,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
3304
3289
|
}
|
|
3305
3290
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
3306
3291
|
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
3307
|
-
|
|
3292
|
+
readFileSync3(htmlPath, "utf-8"),
|
|
3308
3293
|
projectDir,
|
|
3309
3294
|
htmlPath
|
|
3310
3295
|
);
|
|
@@ -3338,7 +3323,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3338
3323
|
const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
|
|
3339
3324
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
3340
3325
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
3341
|
-
const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
|
|
3342
3326
|
const hasAncestorBackgroundImage = detectAncestorBackgroundImage(sanitizedHtml);
|
|
3343
3327
|
const normalizedFontHtml = normalizeSystemFontPrimaryFamilies(
|
|
3344
3328
|
injectTextRenderingRule(
|
|
@@ -3452,7 +3436,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3452
3436
|
renderModeHints,
|
|
3453
3437
|
hasShaderTransitions,
|
|
3454
3438
|
usesThreeDTransforms,
|
|
3455
|
-
usesMixBlendMode,
|
|
3456
3439
|
hasAncestorBackgroundImage
|
|
3457
3440
|
};
|
|
3458
3441
|
}
|
|
@@ -3859,13 +3842,6 @@ async function runCompileStage(input) {
|
|
|
3859
3842
|
"[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
3860
3843
|
);
|
|
3861
3844
|
}
|
|
3862
|
-
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
|
|
3863
|
-
cfg.useDrawElement = false;
|
|
3864
|
-
deCompileGate = "mix_blend_mode";
|
|
3865
|
-
log.info(
|
|
3866
|
-
"[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
|
|
3867
|
-
);
|
|
3868
|
-
}
|
|
3869
3845
|
if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
|
|
3870
3846
|
cfg.useDrawElement = false;
|
|
3871
3847
|
deCompileGate = "ancestor_background_image";
|
|
@@ -5073,7 +5049,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
5073
5049
|
|
|
5074
5050
|
// src/services/render/stages/audioStage.ts
|
|
5075
5051
|
import { join as join10 } from "node:path";
|
|
5076
|
-
import { rmSync as
|
|
5052
|
+
import { rmSync as rmSync4 } from "node:fs";
|
|
5077
5053
|
import {
|
|
5078
5054
|
MIXED_AUDIO_FILENAME,
|
|
5079
5055
|
audioFailureDiagnostics,
|
|
@@ -5082,7 +5058,7 @@ import {
|
|
|
5082
5058
|
|
|
5083
5059
|
// src/services/render/audioPadTrim.ts
|
|
5084
5060
|
import { spawn } from "node:child_process";
|
|
5085
|
-
import { mkdtempSync
|
|
5061
|
+
import { mkdtempSync, rmSync as rmSync3 } from "node:fs";
|
|
5086
5062
|
import { dirname as dirname5, join as join9 } from "node:path";
|
|
5087
5063
|
import {
|
|
5088
5064
|
DEFAULT_CONFIG,
|
|
@@ -5286,7 +5262,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
5286
5262
|
error: `audioPadTrim: failed to materialize ${plan2.operation}: ${err instanceof Error ? err.message : String(err)}`
|
|
5287
5263
|
};
|
|
5288
5264
|
} finally {
|
|
5289
|
-
for (const path of plan2.cleanupPaths)
|
|
5265
|
+
for (const path of plan2.cleanupPaths) rmSync3(path, { force: true });
|
|
5290
5266
|
}
|
|
5291
5267
|
let loweredDb = 0;
|
|
5292
5268
|
if (probeTruePeak) {
|
|
@@ -5323,19 +5299,21 @@ async function constrainAacTruePeak(input) {
|
|
|
5323
5299
|
const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
|
|
5324
5300
|
return enforceAacTruePeak({
|
|
5325
5301
|
audioPath: input.audioPath,
|
|
5302
|
+
sourcePath: input.sourcePath,
|
|
5326
5303
|
targetDurationSeconds: input.targetDurationSeconds,
|
|
5327
5304
|
signal: input.signal,
|
|
5328
|
-
probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
|
|
5329
|
-
encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
5305
|
+
probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
|
|
5306
|
+
encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
|
|
5330
5307
|
});
|
|
5331
5308
|
}
|
|
5332
5309
|
async function enforceAacTruePeak(input) {
|
|
5333
5310
|
let scratchDir;
|
|
5334
5311
|
try {
|
|
5335
|
-
scratchDir =
|
|
5312
|
+
scratchDir = mkdtempSync(join9(dirname5(input.audioPath), ".true-peak-"));
|
|
5336
5313
|
const correctedPath = join9(scratchDir, "audio.m4a");
|
|
5337
5314
|
const measurements = [];
|
|
5338
5315
|
let attenuationDb = 0;
|
|
5316
|
+
let sourcePeakDbfs;
|
|
5339
5317
|
let measuredPath = input.audioPath;
|
|
5340
5318
|
for (let pass = 0; pass <= MAX_TRUE_PEAK_CORRECTION_PASSES; pass += 1) {
|
|
5341
5319
|
const truePeakDbfs = await input.probeTruePeak(measuredPath, input.signal);
|
|
@@ -5352,7 +5330,11 @@ async function enforceAacTruePeak(input) {
|
|
|
5352
5330
|
}
|
|
5353
5331
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
5354
5332
|
const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
|
|
5355
|
-
|
|
5333
|
+
if (input.sourcePath && sourcePeakDbfs === void 0) {
|
|
5334
|
+
sourcePeakDbfs = await input.probeTruePeak(input.sourcePath, input.signal);
|
|
5335
|
+
}
|
|
5336
|
+
const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
|
|
5337
|
+
attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
|
|
5356
5338
|
const result = await input.encode(
|
|
5357
5339
|
input.audioPath,
|
|
5358
5340
|
correctedPath,
|
|
@@ -5384,7 +5366,7 @@ async function enforceAacTruePeak(input) {
|
|
|
5384
5366
|
)}`
|
|
5385
5367
|
};
|
|
5386
5368
|
} finally {
|
|
5387
|
-
if (scratchDir)
|
|
5369
|
+
if (scratchDir) rmSync3(scratchDir, { recursive: true, force: true });
|
|
5388
5370
|
}
|
|
5389
5371
|
}
|
|
5390
5372
|
function failResult(outputPath, target, source, error) {
|
|
@@ -5551,7 +5533,7 @@ async function runAudioStage(input) {
|
|
|
5551
5533
|
}
|
|
5552
5534
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
5553
5535
|
} finally {
|
|
5554
|
-
|
|
5536
|
+
rmSync4(pcmSourcePath, { force: true });
|
|
5555
5537
|
}
|
|
5556
5538
|
}
|
|
5557
5539
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
@@ -5648,7 +5630,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
5648
5630
|
}
|
|
5649
5631
|
|
|
5650
5632
|
// src/services/render/stages/captureStage.ts
|
|
5651
|
-
import { existsSync as existsSync8, readdirSync, statfsSync
|
|
5633
|
+
import { existsSync as existsSync8, readdirSync, statfsSync } from "node:fs";
|
|
5652
5634
|
import { join as join11 } from "node:path";
|
|
5653
5635
|
import {
|
|
5654
5636
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -5670,7 +5652,7 @@ function shouldAllowAdaptiveCaptureRetry(workerCount, _explicitlyConfigured) {
|
|
|
5670
5652
|
var DISK_PROJECTION_SAMPLE_FRAMES = 10;
|
|
5671
5653
|
function freeDiskBytesAt(path) {
|
|
5672
5654
|
try {
|
|
5673
|
-
const stat =
|
|
5655
|
+
const stat = statfsSync(path);
|
|
5674
5656
|
return stat.bavail * stat.bsize;
|
|
5675
5657
|
} catch {
|
|
5676
5658
|
return null;
|
|
@@ -5769,7 +5751,7 @@ async function runCaptureStage(input) {
|
|
|
5769
5751
|
);
|
|
5770
5752
|
}
|
|
5771
5753
|
}
|
|
5772
|
-
const captureOptions = buildCaptureOptions();
|
|
5754
|
+
const captureOptions = { ...buildCaptureOptions(), deFrameFallback: false };
|
|
5773
5755
|
const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
|
|
5774
5756
|
const checkDiskProjection = createDiskCaptureProjection({
|
|
5775
5757
|
framesDir,
|
|
@@ -6030,10 +6012,10 @@ import {
|
|
|
6030
6012
|
import {
|
|
6031
6013
|
copyFileSync as copyFileSync3,
|
|
6032
6014
|
existsSync as existsSync9,
|
|
6033
|
-
mkdirSync as
|
|
6015
|
+
mkdirSync as mkdirSync4,
|
|
6034
6016
|
readdirSync as readdirSync2,
|
|
6035
|
-
readFileSync as
|
|
6036
|
-
rmSync as
|
|
6017
|
+
readFileSync as readFileSync4,
|
|
6018
|
+
rmSync as rmSync5,
|
|
6037
6019
|
statSync as statSync3,
|
|
6038
6020
|
writeFileSync as writeFileSync2
|
|
6039
6021
|
} from "node:fs";
|
|
@@ -6126,7 +6108,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
6126
6108
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
6127
6109
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6128
6110
|
}
|
|
6129
|
-
const gif =
|
|
6111
|
+
const gif = readFileSync4(outputPath);
|
|
6130
6112
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
6131
6113
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
6132
6114
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
@@ -6159,7 +6141,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6159
6141
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6160
6142
|
}
|
|
6161
6143
|
}
|
|
6162
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
6144
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync4(outputPath)) !== false) {
|
|
6163
6145
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
6164
6146
|
if (failure) return failure;
|
|
6165
6147
|
}
|
|
@@ -6172,7 +6154,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6172
6154
|
fileSize
|
|
6173
6155
|
};
|
|
6174
6156
|
} finally {
|
|
6175
|
-
|
|
6157
|
+
rmSync5(input.palettePath, { force: true });
|
|
6176
6158
|
}
|
|
6177
6159
|
}
|
|
6178
6160
|
function startEncodeProgress(job, stage, onProgress) {
|
|
@@ -6322,7 +6304,7 @@ async function runEncodeStage(input) {
|
|
|
6322
6304
|
const stage5Start = Date.now();
|
|
6323
6305
|
if (isPngSequence) {
|
|
6324
6306
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
6325
|
-
if (!existsSync9(outputPath))
|
|
6307
|
+
if (!existsSync9(outputPath)) mkdirSync4(outputPath, { recursive: true });
|
|
6326
6308
|
const captured = readdirSync2(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
6327
6309
|
if (captured.length === 0) {
|
|
6328
6310
|
throw new Error(
|
|
@@ -6778,7 +6760,7 @@ import {
|
|
|
6778
6760
|
// src/services/distributed/shared.ts
|
|
6779
6761
|
import { execFile as execFileCallback } from "node:child_process";
|
|
6780
6762
|
import { dirname as dirname8, join as join14 } from "node:path";
|
|
6781
|
-
import { existsSync as existsSync11, readFileSync as
|
|
6763
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "node:fs";
|
|
6782
6764
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6783
6765
|
import { promisify as promisify2 } from "node:util";
|
|
6784
6766
|
import {
|
|
@@ -7056,7 +7038,7 @@ function readProducerVersion() {
|
|
|
7056
7038
|
const candidate = join14(current, "package.json");
|
|
7057
7039
|
if (existsSync11(candidate)) {
|
|
7058
7040
|
try {
|
|
7059
|
-
const pkg = JSON.parse(
|
|
7041
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
7060
7042
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
7061
7043
|
cachedProducerVersion = pkg.version;
|
|
7062
7044
|
return pkg.version;
|
|
@@ -7347,6 +7329,18 @@ function planFrameWeights(input) {
|
|
|
7347
7329
|
}
|
|
7348
7330
|
return weights;
|
|
7349
7331
|
}
|
|
7332
|
+
function withChunkCaptureCost(slices, weights) {
|
|
7333
|
+
const lightest = weights.reduce((least, weight) => Math.min(least, weight), Infinity);
|
|
7334
|
+
return slices.map((slice) => {
|
|
7335
|
+
let sum2 = 0;
|
|
7336
|
+
for (let frame = slice.startFrame; frame < slice.endFrame; frame++) sum2 += weights[frame];
|
|
7337
|
+
const cost = Math.round(sum2 / (slice.endFrame - slice.startFrame) / lightest * 100) / 100;
|
|
7338
|
+
return cost > 1 ? { ...slice, captureCost: cost } : slice;
|
|
7339
|
+
});
|
|
7340
|
+
}
|
|
7341
|
+
function effectiveChunkFrames(frames, captureCost = 1) {
|
|
7342
|
+
return Math.round(frames * captureCost);
|
|
7343
|
+
}
|
|
7350
7344
|
function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
7351
7345
|
if (isUniformFrameCost(weights)) return null;
|
|
7352
7346
|
const totalFrames = weights.length;
|
|
@@ -7374,7 +7368,7 @@ function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
|
7374
7368
|
}
|
|
7375
7369
|
|
|
7376
7370
|
// src/services/render/stages/freezePlan.ts
|
|
7377
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
7371
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7378
7372
|
import { join as join17, relative as relative3, resolve as resolve7 } from "node:path";
|
|
7379
7373
|
|
|
7380
7374
|
// src/services/distributed/planProtocol.ts
|
|
@@ -7474,7 +7468,7 @@ function readPlanProtocolV1(planJson, capabilities = DISTRIBUTED_RENDER_CAPABILI
|
|
|
7474
7468
|
|
|
7475
7469
|
// src/services/render/stages/planHash.ts
|
|
7476
7470
|
import { createHash as createHash4 } from "node:crypto";
|
|
7477
|
-
import { closeSync as
|
|
7471
|
+
import { closeSync as closeSync2, createReadStream as createReadStream3, openSync as openSync2, readSync } from "node:fs";
|
|
7478
7472
|
var PLAN_HASH_SCHEMA_PREFIX = "hyperframes-plan-hash-v1\0";
|
|
7479
7473
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
7480
7474
|
function computePlanHash(input) {
|
|
@@ -7532,7 +7526,7 @@ function sha256Hex(bytes) {
|
|
|
7532
7526
|
function sha256File(path) {
|
|
7533
7527
|
const hash = createHash4("sha256");
|
|
7534
7528
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
7535
|
-
const fd =
|
|
7529
|
+
const fd = openSync2(path, "r");
|
|
7536
7530
|
try {
|
|
7537
7531
|
let bytesRead = 0;
|
|
7538
7532
|
do {
|
|
@@ -7540,7 +7534,7 @@ function sha256File(path) {
|
|
|
7540
7534
|
if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));
|
|
7541
7535
|
} while (bytesRead > 0);
|
|
7542
7536
|
} finally {
|
|
7543
|
-
|
|
7537
|
+
closeSync2(fd);
|
|
7544
7538
|
}
|
|
7545
7539
|
return hash.digest("hex");
|
|
7546
7540
|
}
|
|
@@ -7614,7 +7608,7 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
7614
7608
|
const assets = [];
|
|
7615
7609
|
for (const file of files) {
|
|
7616
7610
|
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
7617
|
-
compositionHtml =
|
|
7611
|
+
compositionHtml = readFileSync7(file.absolutePath);
|
|
7618
7612
|
continue;
|
|
7619
7613
|
}
|
|
7620
7614
|
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
@@ -7638,7 +7632,7 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
7638
7632
|
}
|
|
7639
7633
|
function readPlanRemoteFiles(planDir) {
|
|
7640
7634
|
const path = join17(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
7641
|
-
return existsSync13(path) ? JSON.parse(
|
|
7635
|
+
return existsSync13(path) ? JSON.parse(readFileSync7(path, "utf-8")) : [];
|
|
7642
7636
|
}
|
|
7643
7637
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
7644
7638
|
const planJsonPath = join17(planDir, "plan.json");
|
|
@@ -7649,8 +7643,8 @@ function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
|
7649
7643
|
if (!existsSync13(encoderJsonPath)) {
|
|
7650
7644
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
7651
7645
|
}
|
|
7652
|
-
const planJson = JSON.parse(
|
|
7653
|
-
const encoderConfigCanonicalJson =
|
|
7646
|
+
const planJson = JSON.parse(readFileSync7(planJsonPath, "utf-8"));
|
|
7647
|
+
const encoderConfigCanonicalJson = readFileSync7(encoderJsonPath, "utf-8");
|
|
7654
7648
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
7655
7649
|
return computePlanHash({
|
|
7656
7650
|
compositionHtml,
|
|
@@ -7680,7 +7674,7 @@ async function freezePlan(input) {
|
|
|
7680
7674
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
7681
7675
|
}
|
|
7682
7676
|
const metaDir = join17(planDir, "meta");
|
|
7683
|
-
if (!existsSync13(metaDir))
|
|
7677
|
+
if (!existsSync13(metaDir)) mkdirSync6(metaDir, { recursive: true });
|
|
7684
7678
|
writeFileSync4(
|
|
7685
7679
|
join17(metaDir, "composition.json"),
|
|
7686
7680
|
`${JSON.stringify(composition, null, 2)}
|
|
@@ -7756,13 +7750,13 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
7756
7750
|
}
|
|
7757
7751
|
|
|
7758
7752
|
// src/services/distributed/chunkAudio.ts
|
|
7759
|
-
import { closeSync as
|
|
7753
|
+
import { closeSync as closeSync3, existsSync as existsSync14, ftruncateSync, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync8 } from "node:fs";
|
|
7760
7754
|
import { writeFileSync as writeFileSync5, writeSync } from "node:fs";
|
|
7761
7755
|
import { join as join19 } from "node:path";
|
|
7762
7756
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
|
|
7763
7757
|
|
|
7764
7758
|
// src/services/distributed/mp4Stitch.ts
|
|
7765
|
-
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as
|
|
7759
|
+
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, rmSync as rmSync7 } from "node:fs";
|
|
7766
7760
|
import { open } from "node:fs/promises";
|
|
7767
7761
|
import { renameWithRetry as renameWithRetry5 } from "@hyperframes/engine";
|
|
7768
7762
|
import { dirname as dirname9, join as join18 } from "node:path";
|
|
@@ -8129,8 +8123,8 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
8129
8123
|
const sourcePaths = audioPath ? [audioPath, ...chunkPaths] : chunkPaths;
|
|
8130
8124
|
const layouts = await Promise.all(sourcePaths.map((path) => readMp4Layout(fileRange(path))));
|
|
8131
8125
|
const header = audioPath ? buildStitchedHeader(layouts.slice(1), layouts[0]) : buildStitchedHeader(layouts);
|
|
8132
|
-
|
|
8133
|
-
const workDir =
|
|
8126
|
+
mkdirSync7(dirname9(outputPath), { recursive: true });
|
|
8127
|
+
const workDir = mkdtempSync2(`${outputPath}.stitch-`);
|
|
8134
8128
|
const out = createWriteStream(join18(workDir, "out.mp4"));
|
|
8135
8129
|
try {
|
|
8136
8130
|
out.write(header);
|
|
@@ -8140,7 +8134,7 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
8140
8134
|
renameWithRetry5(join18(workDir, "out.mp4"), outputPath);
|
|
8141
8135
|
} finally {
|
|
8142
8136
|
out.destroy();
|
|
8143
|
-
|
|
8137
|
+
rmSync7(workDir, { recursive: true, force: true });
|
|
8144
8138
|
}
|
|
8145
8139
|
}
|
|
8146
8140
|
|
|
@@ -8229,9 +8223,9 @@ async function writeChunkAudioSlices(input) {
|
|
|
8229
8223
|
offset += track.sizes[offsets.length - 1];
|
|
8230
8224
|
}
|
|
8231
8225
|
});
|
|
8232
|
-
const bytes =
|
|
8226
|
+
const bytes = readFileSync8(input.audioPath);
|
|
8233
8227
|
const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
|
|
8234
|
-
|
|
8228
|
+
mkdirSync8(join19(input.planDir, SLICE_DIR), { recursive: true });
|
|
8235
8229
|
input.chunks.forEach((_, index) => {
|
|
8236
8230
|
const cut = audioCutForChunk(grid, input.chunks, index, input.fps);
|
|
8237
8231
|
if (!cut) return;
|
|
@@ -8248,9 +8242,9 @@ async function writeChunkAudioSlices(input) {
|
|
|
8248
8242
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
8249
8243
|
const layoutPath = join19(planDir, SLICE_LAYOUT);
|
|
8250
8244
|
if (!existsSync14(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
8251
|
-
const layout = JSON.parse(
|
|
8252
|
-
const frame =
|
|
8253
|
-
const fd =
|
|
8245
|
+
const layout = JSON.parse(readFileSync8(layoutPath, "utf-8"));
|
|
8246
|
+
const frame = readFileSync8(join19(planDir, SLICE_FRAME));
|
|
8247
|
+
const fd = openSync3(scratchPath, "w");
|
|
8254
8248
|
try {
|
|
8255
8249
|
writeSync(fd, frame, 0, layout.payloadStart, 0);
|
|
8256
8250
|
writeSync(
|
|
@@ -8262,12 +8256,12 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
|
8262
8256
|
);
|
|
8263
8257
|
const start = layout.partStarts[chunkIndex];
|
|
8264
8258
|
if (start !== void 0) {
|
|
8265
|
-
const part =
|
|
8259
|
+
const part = readFileSync8(join19(planDir, chunkAudioSlicePath(chunkIndex)));
|
|
8266
8260
|
writeSync(fd, part, 0, part.length, start);
|
|
8267
8261
|
}
|
|
8268
8262
|
ftruncateSync(fd, layout.sizeBytes);
|
|
8269
8263
|
} finally {
|
|
8270
|
-
|
|
8264
|
+
closeSync3(fd);
|
|
8271
8265
|
}
|
|
8272
8266
|
return scratchPath;
|
|
8273
8267
|
}
|
|
@@ -8652,7 +8646,7 @@ var FREEZE_OWNED_PLAN_FILES = [
|
|
|
8652
8646
|
];
|
|
8653
8647
|
function removeFreezeOwnedPlanFiles(planDir) {
|
|
8654
8648
|
for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
|
|
8655
|
-
|
|
8649
|
+
rmSync8(join21(planDir, relativePath), { force: true });
|
|
8656
8650
|
}
|
|
8657
8651
|
}
|
|
8658
8652
|
function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
@@ -8671,7 +8665,10 @@ function buildPlanChunkSlices(frameWeights, config, chunkCount, effectiveChunkSi
|
|
|
8671
8665
|
MIN_CHUNK_SIZE,
|
|
8672
8666
|
Math.min(config.targetChunkFrames ?? MAX_CHUNK_SIZE, MAX_CHUNK_SIZE, maxFramesPerChunk)
|
|
8673
8667
|
) : null;
|
|
8674
|
-
return
|
|
8668
|
+
return withChunkCaptureCost(
|
|
8669
|
+
weighted ?? buildChunkSlices(frameWeights.length, chunkCount, effectiveChunkSize),
|
|
8670
|
+
frameWeights
|
|
8671
|
+
);
|
|
8675
8672
|
}
|
|
8676
8673
|
async function readFontSnapshotSha() {
|
|
8677
8674
|
const module = await import("@hyperframes/core/fonts/embedded-data");
|
|
@@ -8806,7 +8803,7 @@ function createVideoSourceShipper(input) {
|
|
|
8806
8803
|
if (result === null) {
|
|
8807
8804
|
result = `video-sources/${shipped.size}${extname4(current)}`;
|
|
8808
8805
|
const destination = join21(input.planDir, ...result.split("/"));
|
|
8809
|
-
|
|
8806
|
+
mkdirSync9(join21(input.planDir, "video-sources"), { recursive: true });
|
|
8810
8807
|
try {
|
|
8811
8808
|
linkSync(current, destination);
|
|
8812
8809
|
} catch {
|
|
@@ -8826,7 +8823,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8826
8823
|
useGpu: false,
|
|
8827
8824
|
browserGpuMode: "software"
|
|
8828
8825
|
});
|
|
8829
|
-
if (!existsSync15(planDir))
|
|
8826
|
+
if (!existsSync15(planDir)) mkdirSync9(planDir, { recursive: true });
|
|
8830
8827
|
const log = config.logger ?? defaultLogger;
|
|
8831
8828
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
8832
8829
|
const abortSignal = config.abortSignal;
|
|
@@ -8860,10 +8857,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8860
8857
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
8861
8858
|
}
|
|
8862
8859
|
const workDir = join21(planDir, ".plan-work");
|
|
8863
|
-
|
|
8864
|
-
|
|
8860
|
+
rmSync8(workDir, { recursive: true, force: true });
|
|
8861
|
+
mkdirSync9(workDir, { recursive: true });
|
|
8865
8862
|
const compiledDir = join21(workDir, "compiled");
|
|
8866
|
-
|
|
8863
|
+
mkdirSync9(compiledDir, { recursive: true });
|
|
8867
8864
|
let stageStarted = Date.now();
|
|
8868
8865
|
cpSync2(projectDir, compiledDir, {
|
|
8869
8866
|
recursive: true,
|
|
@@ -9067,15 +9064,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9067
9064
|
logPlanStage(log, "extract", stageStarted);
|
|
9068
9065
|
const stagedVideoFrames = join21(compiledDir, "__hyperframes_video_frames");
|
|
9069
9066
|
const videoFramesDst = join21(planDir, "video-frames");
|
|
9070
|
-
if (existsSync15(videoFramesDst))
|
|
9067
|
+
if (existsSync15(videoFramesDst)) rmSync8(videoFramesDst, { recursive: true, force: true });
|
|
9071
9068
|
const videoSourcesDir = join21(planDir, "video-sources");
|
|
9072
|
-
|
|
9069
|
+
rmSync8(videoSourcesDir, { recursive: true, force: true });
|
|
9073
9070
|
if (existsSync15(stagedVideoFrames)) {
|
|
9074
9071
|
renameWithRetry6(stagedVideoFrames, videoFramesDst);
|
|
9075
9072
|
} else {
|
|
9076
|
-
|
|
9073
|
+
mkdirSync9(videoFramesDst, { recursive: true });
|
|
9077
9074
|
}
|
|
9078
|
-
if (existsSync15(finalCompiledDir))
|
|
9075
|
+
if (existsSync15(finalCompiledDir)) rmSync8(finalCompiledDir, { recursive: true, force: true });
|
|
9079
9076
|
renameWithRetry6(compiledDir, finalCompiledDir);
|
|
9080
9077
|
const shipVideoSource = createVideoSourceShipper({
|
|
9081
9078
|
planDir,
|
|
@@ -9105,7 +9102,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9105
9102
|
};
|
|
9106
9103
|
})
|
|
9107
9104
|
});
|
|
9108
|
-
|
|
9105
|
+
mkdirSync9(join21(planDir, "meta"), { recursive: true });
|
|
9109
9106
|
writePlanRemoteFiles(
|
|
9110
9107
|
planDir,
|
|
9111
9108
|
remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
|
|
@@ -9243,7 +9240,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9243
9240
|
format: config.format
|
|
9244
9241
|
};
|
|
9245
9242
|
try {
|
|
9246
|
-
|
|
9243
|
+
rmSync8(workDir, { recursive: true, force: true });
|
|
9247
9244
|
} catch (err) {
|
|
9248
9245
|
log.warn("[plan] failed to remove temp work dir", {
|
|
9249
9246
|
workDir,
|
|
@@ -9313,7 +9310,7 @@ async function buildPlanAudio(projectDir, config, workRoot, outputPath, options
|
|
|
9313
9310
|
renameWithRetry6(mixed.audioOutputPath, outputPath);
|
|
9314
9311
|
return { hasAudio: true };
|
|
9315
9312
|
} finally {
|
|
9316
|
-
|
|
9313
|
+
rmSync8(c.workDir, { recursive: true, force: true });
|
|
9317
9314
|
}
|
|
9318
9315
|
}
|
|
9319
9316
|
async function plan(projectDir, config, planDir, options = {}) {
|
|
@@ -9338,11 +9335,11 @@ import {
|
|
|
9338
9335
|
copyFileSync as copyFileSync7,
|
|
9339
9336
|
existsSync as existsSync17,
|
|
9340
9337
|
lstatSync as lstatSync2,
|
|
9341
|
-
mkdirSync as
|
|
9342
|
-
mkdtempSync as
|
|
9343
|
-
readFileSync as
|
|
9338
|
+
mkdirSync as mkdirSync11,
|
|
9339
|
+
mkdtempSync as mkdtempSync4,
|
|
9340
|
+
readFileSync as readFileSync9,
|
|
9344
9341
|
readdirSync as readdirSync7,
|
|
9345
|
-
rmSync as
|
|
9342
|
+
rmSync as rmSync10,
|
|
9346
9343
|
statSync as statSync6,
|
|
9347
9344
|
writeFileSync as writeFileSync8
|
|
9348
9345
|
} from "node:fs";
|
|
@@ -9356,9 +9353,9 @@ import {
|
|
|
9356
9353
|
copyFileSync as copyFileSync6,
|
|
9357
9354
|
existsSync as existsSync16,
|
|
9358
9355
|
linkSync as linkSync2,
|
|
9359
|
-
mkdirSync as
|
|
9360
|
-
mkdtempSync as
|
|
9361
|
-
rmSync as
|
|
9356
|
+
mkdirSync as mkdirSync10,
|
|
9357
|
+
mkdtempSync as mkdtempSync3,
|
|
9358
|
+
rmSync as rmSync9,
|
|
9362
9359
|
statSync as statSync5,
|
|
9363
9360
|
writeFileSync as writeFileSync7
|
|
9364
9361
|
} from "node:fs";
|
|
@@ -9428,8 +9425,8 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9428
9425
|
}
|
|
9429
9426
|
this.destinationDir = destinationDir;
|
|
9430
9427
|
this.#linkFile = options.linkFile ?? linkSync2;
|
|
9431
|
-
|
|
9432
|
-
this.temporaryDir =
|
|
9428
|
+
mkdirSync10(dirname10(destinationDir), { recursive: true });
|
|
9429
|
+
this.temporaryDir = mkdtempSync3(join23(dirname10(destinationDir), ".plan-v2-publish-"));
|
|
9433
9430
|
}
|
|
9434
9431
|
async putBlob(blob) {
|
|
9435
9432
|
const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
|
|
@@ -9441,8 +9438,8 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9441
9438
|
}
|
|
9442
9439
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
9443
9440
|
if (existsSync16(destinationPath)) return;
|
|
9444
|
-
|
|
9445
|
-
const stagingDir =
|
|
9441
|
+
mkdirSync10(dirname10(destinationPath), { recursive: true });
|
|
9442
|
+
const stagingDir = mkdtempSync3(join23(dirname10(destinationPath), ".plan-v2-blob-"));
|
|
9446
9443
|
const temporaryPath = join23(stagingDir, "blob");
|
|
9447
9444
|
try {
|
|
9448
9445
|
try {
|
|
@@ -9453,7 +9450,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9453
9450
|
}
|
|
9454
9451
|
renameWithRetry7(temporaryPath, destinationPath);
|
|
9455
9452
|
} finally {
|
|
9456
|
-
|
|
9453
|
+
rmSync9(stagingDir, { recursive: true, force: true });
|
|
9457
9454
|
}
|
|
9458
9455
|
}
|
|
9459
9456
|
async commitManifest(manifestBytes) {
|
|
@@ -9470,7 +9467,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9470
9467
|
}
|
|
9471
9468
|
async abort() {
|
|
9472
9469
|
if (!this.#committed) {
|
|
9473
|
-
|
|
9470
|
+
rmSync9(this.temporaryDir, { recursive: true, force: true });
|
|
9474
9471
|
}
|
|
9475
9472
|
}
|
|
9476
9473
|
};
|
|
@@ -9489,7 +9486,7 @@ function isNonNegativeIntegerArray(value) {
|
|
|
9489
9486
|
return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
|
|
9490
9487
|
}
|
|
9491
9488
|
function readJsonFile(path, label) {
|
|
9492
|
-
const contents =
|
|
9489
|
+
const contents = readFileSync9(path, "utf-8");
|
|
9493
9490
|
try {
|
|
9494
9491
|
const parsed = JSON.parse(contents);
|
|
9495
9492
|
return parsed;
|
|
@@ -9641,7 +9638,7 @@ function materializeExtractedVideoDirectories(planDir) {
|
|
|
9641
9638
|
if (!existsSync17(videosPath)) return;
|
|
9642
9639
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
9643
9640
|
for (const video of videos.extracted) {
|
|
9644
|
-
|
|
9641
|
+
mkdirSync11(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
9645
9642
|
}
|
|
9646
9643
|
}
|
|
9647
9644
|
function parseChunkSlices(value) {
|
|
@@ -9662,7 +9659,12 @@ function parseChunkSlices(value) {
|
|
|
9662
9659
|
throw new PlanV2IntegrityError(`meta/chunks.json contains duplicate chunk index ${index}`);
|
|
9663
9660
|
}
|
|
9664
9661
|
indexes.add(index);
|
|
9665
|
-
return { index, startFrame, endFrame };
|
|
9662
|
+
if (chunk.captureCost === void 0) return { index, startFrame, endFrame };
|
|
9663
|
+
const captureCost = chunk.captureCost;
|
|
9664
|
+
if (typeof captureCost !== "number" || !Number.isFinite(captureCost) || captureCost <= 1) {
|
|
9665
|
+
throw new PlanV2IntegrityError(`${field}.captureCost must be a number above 1`);
|
|
9666
|
+
}
|
|
9667
|
+
return { index, startFrame, endFrame, captureCost };
|
|
9666
9668
|
});
|
|
9667
9669
|
}
|
|
9668
9670
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
@@ -9727,14 +9729,14 @@ function computeManifestHash(manifest) {
|
|
|
9727
9729
|
}
|
|
9728
9730
|
function writeBlob(sourcePath, destinationPath) {
|
|
9729
9731
|
if (existsSync17(destinationPath)) return;
|
|
9730
|
-
|
|
9731
|
-
const temporaryDir =
|
|
9732
|
+
mkdirSync11(dirname11(destinationPath), { recursive: true });
|
|
9733
|
+
const temporaryDir = mkdtempSync4(join24(dirname11(destinationPath), ".plan-v2-blob-"));
|
|
9732
9734
|
const temporaryPath = join24(temporaryDir, "blob");
|
|
9733
9735
|
try {
|
|
9734
9736
|
copyFileSync7(sourcePath, temporaryPath);
|
|
9735
9737
|
renameWithRetry8(temporaryPath, destinationPath);
|
|
9736
9738
|
} finally {
|
|
9737
|
-
|
|
9739
|
+
rmSync10(temporaryDir, { recursive: true, force: true });
|
|
9738
9740
|
}
|
|
9739
9741
|
}
|
|
9740
9742
|
function publishBlobFor(absolutePath, digests) {
|
|
@@ -9853,8 +9855,8 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
9853
9855
|
if (publication.storedBlobs.length > 0) {
|
|
9854
9856
|
throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
|
|
9855
9857
|
}
|
|
9856
|
-
|
|
9857
|
-
const tempDir =
|
|
9858
|
+
mkdirSync11(dirname11(planV2Dir), { recursive: true });
|
|
9859
|
+
const tempDir = mkdtempSync4(join24(dirname11(planV2Dir), ".plan-v2-build-"));
|
|
9858
9860
|
try {
|
|
9859
9861
|
for (const blob of publication.blobs) {
|
|
9860
9862
|
writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
|
|
@@ -9867,7 +9869,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
9867
9869
|
renameWithRetry8(tempDir, planV2Dir);
|
|
9868
9870
|
return resultFromManifest(planV2Dir, publication.manifest);
|
|
9869
9871
|
} catch (error) {
|
|
9870
|
-
|
|
9872
|
+
rmSync10(tempDir, { recursive: true, force: true });
|
|
9871
9873
|
throw error;
|
|
9872
9874
|
}
|
|
9873
9875
|
}
|
|
@@ -9907,7 +9909,7 @@ async function publishPlanV2FromV1(executionPlanDir, publisher) {
|
|
|
9907
9909
|
return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
|
|
9908
9910
|
}
|
|
9909
9911
|
async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
|
|
9910
|
-
const stagingRoot =
|
|
9912
|
+
const stagingRoot = mkdtempSync4(join24(options.stagingParentDir ?? tmpdir(), ".execution-plan-"));
|
|
9911
9913
|
const digests = /* @__PURE__ */ new Map();
|
|
9912
9914
|
try {
|
|
9913
9915
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
@@ -9934,7 +9936,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
9934
9936
|
}
|
|
9935
9937
|
throw error;
|
|
9936
9938
|
} finally {
|
|
9937
|
-
|
|
9939
|
+
rmSync10(stagingRoot, { recursive: true, force: true });
|
|
9938
9940
|
}
|
|
9939
9941
|
}
|
|
9940
9942
|
async function planV2(projectDir, config, planV2Dir) {
|
|
@@ -10131,12 +10133,12 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
10131
10133
|
artifact,
|
|
10132
10134
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
10133
10135
|
}));
|
|
10134
|
-
|
|
10135
|
-
const tempDir =
|
|
10136
|
+
mkdirSync11(dirname11(destinationDir), { recursive: true });
|
|
10137
|
+
const tempDir = mkdtempSync4(join24(dirname11(destinationDir), ".plan-v2-materialize-"));
|
|
10136
10138
|
try {
|
|
10137
10139
|
for (const { artifact, sourcePath } of verified) {
|
|
10138
10140
|
const destinationPath = join24(tempDir, ...artifact.path.split("/"));
|
|
10139
|
-
|
|
10141
|
+
mkdirSync11(dirname11(destinationPath), { recursive: true });
|
|
10140
10142
|
copyFileSync7(sourcePath, destinationPath);
|
|
10141
10143
|
}
|
|
10142
10144
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
@@ -10151,7 +10153,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
10151
10153
|
);
|
|
10152
10154
|
renameWithRetry8(tempDir, destinationDir);
|
|
10153
10155
|
} catch (error) {
|
|
10154
|
-
|
|
10156
|
+
rmSync10(tempDir, { recursive: true, force: true });
|
|
10155
10157
|
throw error;
|
|
10156
10158
|
}
|
|
10157
10159
|
return {
|
|
@@ -10215,7 +10217,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
10215
10217
|
}
|
|
10216
10218
|
|
|
10217
10219
|
// src/services/distributed/planV2Execution.ts
|
|
10218
|
-
import { mkdtempSync as
|
|
10220
|
+
import { mkdtempSync as mkdtempSync6, rmSync as rmSync13 } from "node:fs";
|
|
10219
10221
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10220
10222
|
import { join as join27 } from "node:path";
|
|
10221
10223
|
|
|
@@ -10223,11 +10225,11 @@ import { join as join27 } from "node:path";
|
|
|
10223
10225
|
import {
|
|
10224
10226
|
cpSync as cpSync3,
|
|
10225
10227
|
existsSync as existsSync18,
|
|
10226
|
-
mkdirSync as
|
|
10227
|
-
mkdtempSync as
|
|
10228
|
-
readFileSync as
|
|
10228
|
+
mkdirSync as mkdirSync12,
|
|
10229
|
+
mkdtempSync as mkdtempSync5,
|
|
10230
|
+
readFileSync as readFileSync10,
|
|
10229
10231
|
readdirSync as readdirSync8,
|
|
10230
|
-
rmSync as
|
|
10232
|
+
rmSync as rmSync11,
|
|
10231
10233
|
statSync as statSync7,
|
|
10232
10234
|
writeFileSync as writeFileSync9
|
|
10233
10235
|
} from "node:fs";
|
|
@@ -10273,9 +10275,9 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
10273
10275
|
const format = plan2.dimensions.format;
|
|
10274
10276
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
10275
10277
|
if (!existsSync18(dirname12(outputPath))) {
|
|
10276
|
-
|
|
10278
|
+
mkdirSync12(dirname12(outputPath), { recursive: true });
|
|
10277
10279
|
}
|
|
10278
|
-
const workDir =
|
|
10280
|
+
const workDir = mkdtempSync5(`${outputPath}.assemble-work-`);
|
|
10279
10281
|
const fps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
|
|
10280
10282
|
const stageMs = {};
|
|
10281
10283
|
const ctx = {
|
|
@@ -10306,7 +10308,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
10306
10308
|
});
|
|
10307
10309
|
} finally {
|
|
10308
10310
|
try {
|
|
10309
|
-
|
|
10311
|
+
rmSync11(workDir, { recursive: true, force: true });
|
|
10310
10312
|
} catch (err) {
|
|
10311
10313
|
log.warn("[assemble] failed to remove work dir", {
|
|
10312
10314
|
workDir,
|
|
@@ -10328,13 +10330,13 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
10328
10330
|
if (!existsSync18(planJsonPath)) {
|
|
10329
10331
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
10330
10332
|
}
|
|
10331
|
-
const plan2 = JSON.parse(
|
|
10333
|
+
const plan2 = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
10332
10334
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
10333
10335
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
10334
10336
|
if (!existsSync18(chunksJsonPath)) {
|
|
10335
10337
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
10336
10338
|
}
|
|
10337
|
-
const chunks = JSON.parse(
|
|
10339
|
+
const chunks = JSON.parse(readFileSync10(chunksJsonPath, "utf-8"));
|
|
10338
10340
|
if (chunkPaths.length !== chunks.length) {
|
|
10339
10341
|
throw new Error(
|
|
10340
10342
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
@@ -10477,13 +10479,13 @@ function stitchesSeparateAudio(planDir) {
|
|
|
10477
10479
|
return lockedAudio(planDir).separateAudio === true;
|
|
10478
10480
|
}
|
|
10479
10481
|
function planFormat(planDir) {
|
|
10480
|
-
const plan2 = JSON.parse(
|
|
10482
|
+
const plan2 = JSON.parse(readFileSync10(join25(planDir, "plan.json"), "utf-8"));
|
|
10481
10483
|
return plan2.dimensions.format;
|
|
10482
10484
|
}
|
|
10483
10485
|
function lockedAudio(planDir) {
|
|
10484
10486
|
const encoderJsonPath = join25(planDir, "meta", "encoder.json");
|
|
10485
10487
|
if (!existsSync18(encoderJsonPath)) return {};
|
|
10486
|
-
return JSON.parse(
|
|
10488
|
+
return JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
|
|
10487
10489
|
}
|
|
10488
10490
|
function readCfrEncoder(planDir, format) {
|
|
10489
10491
|
if (format !== "mp4") {
|
|
@@ -10495,7 +10497,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
10495
10497
|
if (!existsSync18(encoderJsonPath)) {
|
|
10496
10498
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
10497
10499
|
}
|
|
10498
|
-
const encoderJson = JSON.parse(
|
|
10500
|
+
const encoderJson = JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
|
|
10499
10501
|
if (encoderJson.encoder === "libx265-software") {
|
|
10500
10502
|
throw new Error(
|
|
10501
10503
|
`[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
|
|
@@ -10504,8 +10506,8 @@ function readCfrEncoder(planDir, format) {
|
|
|
10504
10506
|
return encoderJson;
|
|
10505
10507
|
}
|
|
10506
10508
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
10507
|
-
if (existsSync18(outputPath))
|
|
10508
|
-
|
|
10509
|
+
if (existsSync18(outputPath)) rmSync11(outputPath, { recursive: true, force: true });
|
|
10510
|
+
mkdirSync12(outputPath, { recursive: true });
|
|
10509
10511
|
let globalIdx = 0;
|
|
10510
10512
|
for (const chunkDir of chunkPaths) {
|
|
10511
10513
|
if (!statSync7(chunkDir).isDirectory()) {
|
|
@@ -10549,7 +10551,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
10549
10551
|
|
|
10550
10552
|
// src/services/distributed/renderChunk.ts
|
|
10551
10553
|
import { randomBytes } from "node:crypto";
|
|
10552
|
-
import { existsSync as existsSync19, mkdirSync as
|
|
10554
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync11, readdirSync as readdirSync9, rmSync as rmSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10553
10555
|
import { cpus } from "node:os";
|
|
10554
10556
|
import { join as join26 } from "node:path";
|
|
10555
10557
|
import {
|
|
@@ -10667,10 +10669,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
|
|
|
10667
10669
|
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
10668
10670
|
}
|
|
10669
10671
|
function hashChunkOutput(outputPath, kind) {
|
|
10670
|
-
if (kind === "file") return sha256Hex(
|
|
10672
|
+
if (kind === "file") return sha256Hex(readFileSync11(outputPath));
|
|
10671
10673
|
const entries = readdirSync9(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
10672
10674
|
const lines = entries.map(
|
|
10673
|
-
(name) => `${name}\0${sha256Hex(
|
|
10675
|
+
(name) => `${name}\0${sha256Hex(readFileSync11(join26(outputPath, name)))}`
|
|
10674
10676
|
);
|
|
10675
10677
|
return sha256Hex(lines.join("\0"));
|
|
10676
10678
|
}
|
|
@@ -10696,7 +10698,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10696
10698
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
10697
10699
|
);
|
|
10698
10700
|
}
|
|
10699
|
-
const plan2 = JSON.parse(
|
|
10701
|
+
const plan2 = JSON.parse(readFileSync11(planJsonPath, "utf-8"));
|
|
10700
10702
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
10701
10703
|
const planHashStarted = Date.now();
|
|
10702
10704
|
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
@@ -10713,13 +10715,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10713
10715
|
);
|
|
10714
10716
|
}
|
|
10715
10717
|
}
|
|
10716
|
-
const encoder = JSON.parse(
|
|
10717
|
-
const chunks = JSON.parse(
|
|
10718
|
+
const encoder = JSON.parse(readFileSync11(encoderJsonPath, "utf-8"));
|
|
10719
|
+
const chunks = JSON.parse(readFileSync11(chunksJsonPath, "utf-8"));
|
|
10718
10720
|
const videosJsonPath = join26(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
10719
10721
|
let planVideos = null;
|
|
10720
10722
|
if (existsSync19(videosJsonPath)) {
|
|
10721
10723
|
try {
|
|
10722
|
-
planVideos = validatePlanVideosForChunk(JSON.parse(
|
|
10724
|
+
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync11(videosJsonPath, "utf-8")));
|
|
10723
10725
|
} catch (err) {
|
|
10724
10726
|
if (err instanceof RenderChunkValidationError) throw err;
|
|
10725
10727
|
throw new RenderChunkValidationError(
|
|
@@ -10804,9 +10806,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10804
10806
|
forceScreenshotExplicitlyOptedOut: !encoder.forceScreenshot
|
|
10805
10807
|
};
|
|
10806
10808
|
const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
10807
|
-
|
|
10809
|
+
mkdirSync13(workDir, { recursive: true });
|
|
10808
10810
|
const framesDir = join26(workDir, "captured-frames");
|
|
10809
|
-
|
|
10811
|
+
mkdirSync13(framesDir, { recursive: true });
|
|
10810
10812
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
10811
10813
|
const chunkFrameNumbers = Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i);
|
|
10812
10814
|
const chunkFps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
|
|
@@ -10830,7 +10832,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10830
10832
|
dimensions: plan2.dimensions,
|
|
10831
10833
|
encoder,
|
|
10832
10834
|
videoCount: planVideos?.videos.length ?? 0,
|
|
10833
|
-
html:
|
|
10835
|
+
html: readFileSync11(join26(compiledDir, "index.html"), "utf-8"),
|
|
10834
10836
|
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
10835
10837
|
skipReadinessVideoIds: deferredVideos.filter(
|
|
10836
10838
|
(video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
|
|
@@ -10839,15 +10841,17 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10839
10841
|
deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
|
|
10840
10842
|
)
|
|
10841
10843
|
});
|
|
10844
|
+
const captureFrames = effectiveChunkFrames(framesInChunk, slice.captureCost);
|
|
10842
10845
|
const chunkWorkerCount = resolveChunkCaptureWorkers(
|
|
10843
|
-
|
|
10844
|
-
calculateOptimalWorkers2(
|
|
10846
|
+
captureFrames,
|
|
10847
|
+
calculateOptimalWorkers2(captureFrames, void 0, cfg),
|
|
10845
10848
|
options?.maxCaptureWorkers
|
|
10846
10849
|
);
|
|
10847
10850
|
let session = null;
|
|
10848
10851
|
let outputKind;
|
|
10849
10852
|
let framesEncoded = 0;
|
|
10850
10853
|
let sessionBootMs = 0;
|
|
10854
|
+
let videoExtractMs = 0;
|
|
10851
10855
|
let captureStageMs = 0;
|
|
10852
10856
|
let encodeStageMs = 0;
|
|
10853
10857
|
let captureMode;
|
|
@@ -10913,6 +10917,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10913
10917
|
session = null;
|
|
10914
10918
|
}
|
|
10915
10919
|
}
|
|
10920
|
+
const extractStarted = Date.now();
|
|
10916
10921
|
const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
|
|
10917
10922
|
planDir,
|
|
10918
10923
|
planVideos,
|
|
@@ -10922,6 +10927,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10922
10927
|
cfg,
|
|
10923
10928
|
remoteSources,
|
|
10924
10929
|
shownBoxes
|
|
10930
|
+
}).then((frames) => {
|
|
10931
|
+
videoExtractMs = Date.now() - extractStarted;
|
|
10932
|
+
return frames;
|
|
10925
10933
|
}) : Promise.resolve([]);
|
|
10926
10934
|
extraction = deferredVideoFrames;
|
|
10927
10935
|
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
|
|
@@ -10953,10 +10961,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10953
10961
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
10954
10962
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
10955
10963
|
if (isPngSequence) {
|
|
10956
|
-
if (!existsSync19(outputChunkPath))
|
|
10964
|
+
if (!existsSync19(outputChunkPath)) mkdirSync13(outputChunkPath, { recursive: true });
|
|
10957
10965
|
} else {
|
|
10958
10966
|
const outDir = join26(outputChunkPath, "..");
|
|
10959
|
-
if (!existsSync19(outDir))
|
|
10967
|
+
if (!existsSync19(outDir)) mkdirSync13(outDir, { recursive: true });
|
|
10960
10968
|
}
|
|
10961
10969
|
const encodeInput = {
|
|
10962
10970
|
job,
|
|
@@ -11059,8 +11067,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11059
11067
|
return observedChunkCaptureMode(capturePerfs);
|
|
11060
11068
|
},
|
|
11061
11069
|
resetForScreenshotRetry: () => {
|
|
11062
|
-
|
|
11063
|
-
|
|
11070
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11071
|
+
mkdirSync13(framesDir, { recursive: true });
|
|
11064
11072
|
capturePerfs.length = 0;
|
|
11065
11073
|
captureAttempts.length = 0;
|
|
11066
11074
|
job.framesRendered = 0;
|
|
@@ -11102,8 +11110,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11102
11110
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
11103
11111
|
timeoutMs: cfg.ffmpegProcessTimeout
|
|
11104
11112
|
});
|
|
11105
|
-
|
|
11106
|
-
|
|
11113
|
+
rmSync12(videoOnlyPath, { force: true });
|
|
11114
|
+
rmSync12(audioScratchPath, { force: true });
|
|
11107
11115
|
}
|
|
11108
11116
|
encodeStageMs = Date.now() - encodeStarted;
|
|
11109
11117
|
} finally {
|
|
@@ -11139,6 +11147,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11139
11147
|
sessionBootMs,
|
|
11140
11148
|
captureStageMs,
|
|
11141
11149
|
encodeStageMs,
|
|
11150
|
+
// Wall time of the chunk's own video frame extraction; it overlaps captureStageMs, so do not add them.
|
|
11151
|
+
videoExtractMs,
|
|
11142
11152
|
workers: chunkWorkerCount,
|
|
11143
11153
|
captureMode,
|
|
11144
11154
|
// Device-pixel box each video's frames were extracted to cover; a video not listed kept full size.
|
|
@@ -11151,8 +11161,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11151
11161
|
writeFileSync10(perfPath, `${JSON.stringify(perfPayload, null, 2)}
|
|
11152
11162
|
`, "utf-8");
|
|
11153
11163
|
try {
|
|
11154
|
-
|
|
11155
|
-
|
|
11164
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11165
|
+
rmSync12(videoFramesDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11156
11166
|
} catch (err) {
|
|
11157
11167
|
log.warn("[renderChunk] failed to remove work dir", {
|
|
11158
11168
|
workDir,
|
|
@@ -11181,17 +11191,17 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11181
11191
|
|
|
11182
11192
|
// src/services/distributed/planV2Execution.ts
|
|
11183
11193
|
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
11184
|
-
const workRoot =
|
|
11194
|
+
const workRoot = mkdtempSync6(join27(tmpdir2(), "hf-plan-v2-chunk-"));
|
|
11185
11195
|
const materializedPlanDir = join27(workRoot, "plan");
|
|
11186
11196
|
try {
|
|
11187
11197
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
11188
11198
|
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
11189
11199
|
} finally {
|
|
11190
|
-
|
|
11200
|
+
rmSync13(workRoot, { recursive: true, force: true });
|
|
11191
11201
|
}
|
|
11192
11202
|
}
|
|
11193
11203
|
async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
11194
|
-
const workRoot =
|
|
11204
|
+
const workRoot = mkdtempSync6(join27(tmpdir2(), "hf-plan-v2-assembler-"));
|
|
11195
11205
|
const materializedPlanDir = join27(workRoot, "plan");
|
|
11196
11206
|
try {
|
|
11197
11207
|
const materialized = materializePlanV2Target(
|
|
@@ -11207,7 +11217,7 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
|
11207
11217
|
options
|
|
11208
11218
|
);
|
|
11209
11219
|
} finally {
|
|
11210
|
-
|
|
11220
|
+
rmSync13(workRoot, { recursive: true, force: true });
|
|
11211
11221
|
}
|
|
11212
11222
|
}
|
|
11213
11223
|
|
|
@@ -11456,7 +11466,7 @@ function validateIntDimension(field, value) {
|
|
|
11456
11466
|
}
|
|
11457
11467
|
|
|
11458
11468
|
// src/services/distributed/projectHash.ts
|
|
11459
|
-
import { closeSync as
|
|
11469
|
+
import { closeSync as closeSync4, openSync as openSync4, readdirSync as readdirSync10, readSync as readSync3, realpathSync as realpathSync2, statSync as statSync8 } from "node:fs";
|
|
11460
11470
|
import { createHash as createHash6 } from "node:crypto";
|
|
11461
11471
|
import { join as join28 } from "node:path";
|
|
11462
11472
|
var SITE_ID_SCHEME = "site-v2\0";
|
|
@@ -11497,14 +11507,14 @@ function hashProjectDir(projectDir, files = listSiteFiles(projectDir)) {
|
|
|
11497
11507
|
for (const rel of files) {
|
|
11498
11508
|
hash.update(rel);
|
|
11499
11509
|
hash.update("\0");
|
|
11500
|
-
const fd =
|
|
11510
|
+
const fd = openSync4(join28(projectDir, rel), "r");
|
|
11501
11511
|
try {
|
|
11502
11512
|
let read;
|
|
11503
11513
|
while ((read = readSync3(fd, chunk, 0, chunk.length, null)) > 0) {
|
|
11504
11514
|
hash.update(chunk.subarray(0, read));
|
|
11505
11515
|
}
|
|
11506
11516
|
} finally {
|
|
11507
|
-
|
|
11517
|
+
closeSync4(fd);
|
|
11508
11518
|
}
|
|
11509
11519
|
}
|
|
11510
11520
|
return hash.digest("hex").slice(0, 16);
|