@hyperframes/producer 0.8.197 → 0.8.199
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/README.md +1 -1
- package/dist/distributed.js +232 -150
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +59 -59
- package/dist/index.js +748 -431
- package/dist/public-server.js +495 -268
- package/dist/renderRequest.d.ts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/services/distributed/chunkWeights.d.ts +16 -0
- package/dist/services/distributed/plan.d.ts +7 -0
- package/dist/services/hdrCompositor.d.ts +5 -5
- package/dist/services/hdrFrameWindows.d.ts +23 -0
- package/dist/services/render/renderEtaGuard.d.ts +35 -0
- package/dist/services/render/stages/captureHdrResources.d.ts +5 -17
- package/dist/services/renderOrchestrator.d.ts +5 -5
- 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));
|
|
@@ -2375,7 +2364,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
2375
2364
|
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
2376
2365
|
let filePath = src;
|
|
2377
2366
|
if (isHttpUrl(src)) {
|
|
2378
|
-
if (!existsSync6(downloadDir))
|
|
2367
|
+
if (!existsSync6(downloadDir)) mkdirSync3(downloadDir, { recursive: true });
|
|
2379
2368
|
try {
|
|
2380
2369
|
filePath = await downloadToTemp(src, downloadDir, void 0, void 0, void 0, {
|
|
2381
2370
|
onTelemetry: logRemoteDownloadTelemetry
|
|
@@ -2958,7 +2947,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
|
|
|
2958
2947
|
var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
2959
2948
|
async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
|
|
2960
2949
|
if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
|
|
2961
|
-
if (!existsSync6(remoteDir))
|
|
2950
|
+
if (!existsSync6(remoteDir)) mkdirSync3(remoteDir, { recursive: true });
|
|
2962
2951
|
const urlToLocal = /* @__PURE__ */ new Map();
|
|
2963
2952
|
await Promise.all(
|
|
2964
2953
|
[...urlSet].map(async (url) => {
|
|
@@ -3304,7 +3293,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
3304
3293
|
}
|
|
3305
3294
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
3306
3295
|
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
3307
|
-
|
|
3296
|
+
readFileSync3(htmlPath, "utf-8"),
|
|
3308
3297
|
projectDir,
|
|
3309
3298
|
htmlPath
|
|
3310
3299
|
);
|
|
@@ -5073,7 +5062,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
5073
5062
|
|
|
5074
5063
|
// src/services/render/stages/audioStage.ts
|
|
5075
5064
|
import { join as join10 } from "node:path";
|
|
5076
|
-
import { rmSync as
|
|
5065
|
+
import { rmSync as rmSync4 } from "node:fs";
|
|
5077
5066
|
import {
|
|
5078
5067
|
MIXED_AUDIO_FILENAME,
|
|
5079
5068
|
audioFailureDiagnostics,
|
|
@@ -5082,7 +5071,7 @@ import {
|
|
|
5082
5071
|
|
|
5083
5072
|
// src/services/render/audioPadTrim.ts
|
|
5084
5073
|
import { spawn } from "node:child_process";
|
|
5085
|
-
import { mkdtempSync
|
|
5074
|
+
import { mkdtempSync, rmSync as rmSync3 } from "node:fs";
|
|
5086
5075
|
import { dirname as dirname5, join as join9 } from "node:path";
|
|
5087
5076
|
import {
|
|
5088
5077
|
DEFAULT_CONFIG,
|
|
@@ -5286,7 +5275,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
5286
5275
|
error: `audioPadTrim: failed to materialize ${plan2.operation}: ${err instanceof Error ? err.message : String(err)}`
|
|
5287
5276
|
};
|
|
5288
5277
|
} finally {
|
|
5289
|
-
for (const path of plan2.cleanupPaths)
|
|
5278
|
+
for (const path of plan2.cleanupPaths) rmSync3(path, { force: true });
|
|
5290
5279
|
}
|
|
5291
5280
|
let loweredDb = 0;
|
|
5292
5281
|
if (probeTruePeak) {
|
|
@@ -5332,7 +5321,7 @@ async function constrainAacTruePeak(input) {
|
|
|
5332
5321
|
async function enforceAacTruePeak(input) {
|
|
5333
5322
|
let scratchDir;
|
|
5334
5323
|
try {
|
|
5335
|
-
scratchDir =
|
|
5324
|
+
scratchDir = mkdtempSync(join9(dirname5(input.audioPath), ".true-peak-"));
|
|
5336
5325
|
const correctedPath = join9(scratchDir, "audio.m4a");
|
|
5337
5326
|
const measurements = [];
|
|
5338
5327
|
let attenuationDb = 0;
|
|
@@ -5384,7 +5373,7 @@ async function enforceAacTruePeak(input) {
|
|
|
5384
5373
|
)}`
|
|
5385
5374
|
};
|
|
5386
5375
|
} finally {
|
|
5387
|
-
if (scratchDir)
|
|
5376
|
+
if (scratchDir) rmSync3(scratchDir, { recursive: true, force: true });
|
|
5388
5377
|
}
|
|
5389
5378
|
}
|
|
5390
5379
|
function failResult(outputPath, target, source, error) {
|
|
@@ -5551,7 +5540,7 @@ async function runAudioStage(input) {
|
|
|
5551
5540
|
}
|
|
5552
5541
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
5553
5542
|
} finally {
|
|
5554
|
-
|
|
5543
|
+
rmSync4(pcmSourcePath, { force: true });
|
|
5555
5544
|
}
|
|
5556
5545
|
}
|
|
5557
5546
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
@@ -5648,7 +5637,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
5648
5637
|
}
|
|
5649
5638
|
|
|
5650
5639
|
// src/services/render/stages/captureStage.ts
|
|
5651
|
-
import { existsSync as existsSync8, readdirSync, statfsSync
|
|
5640
|
+
import { existsSync as existsSync8, readdirSync, statfsSync } from "node:fs";
|
|
5652
5641
|
import { join as join11 } from "node:path";
|
|
5653
5642
|
import {
|
|
5654
5643
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -5670,7 +5659,7 @@ function shouldAllowAdaptiveCaptureRetry(workerCount, _explicitlyConfigured) {
|
|
|
5670
5659
|
var DISK_PROJECTION_SAMPLE_FRAMES = 10;
|
|
5671
5660
|
function freeDiskBytesAt(path) {
|
|
5672
5661
|
try {
|
|
5673
|
-
const stat =
|
|
5662
|
+
const stat = statfsSync(path);
|
|
5674
5663
|
return stat.bavail * stat.bsize;
|
|
5675
5664
|
} catch {
|
|
5676
5665
|
return null;
|
|
@@ -6030,10 +6019,10 @@ import {
|
|
|
6030
6019
|
import {
|
|
6031
6020
|
copyFileSync as copyFileSync3,
|
|
6032
6021
|
existsSync as existsSync9,
|
|
6033
|
-
mkdirSync as
|
|
6022
|
+
mkdirSync as mkdirSync4,
|
|
6034
6023
|
readdirSync as readdirSync2,
|
|
6035
|
-
readFileSync as
|
|
6036
|
-
rmSync as
|
|
6024
|
+
readFileSync as readFileSync4,
|
|
6025
|
+
rmSync as rmSync5,
|
|
6037
6026
|
statSync as statSync3,
|
|
6038
6027
|
writeFileSync as writeFileSync2
|
|
6039
6028
|
} from "node:fs";
|
|
@@ -6126,7 +6115,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
6126
6115
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
6127
6116
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6128
6117
|
}
|
|
6129
|
-
const gif =
|
|
6118
|
+
const gif = readFileSync4(outputPath);
|
|
6130
6119
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
6131
6120
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
6132
6121
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
@@ -6159,7 +6148,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6159
6148
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6160
6149
|
}
|
|
6161
6150
|
}
|
|
6162
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
6151
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync4(outputPath)) !== false) {
|
|
6163
6152
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
6164
6153
|
if (failure) return failure;
|
|
6165
6154
|
}
|
|
@@ -6172,7 +6161,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6172
6161
|
fileSize
|
|
6173
6162
|
};
|
|
6174
6163
|
} finally {
|
|
6175
|
-
|
|
6164
|
+
rmSync5(input.palettePath, { force: true });
|
|
6176
6165
|
}
|
|
6177
6166
|
}
|
|
6178
6167
|
function startEncodeProgress(job, stage, onProgress) {
|
|
@@ -6322,7 +6311,7 @@ async function runEncodeStage(input) {
|
|
|
6322
6311
|
const stage5Start = Date.now();
|
|
6323
6312
|
if (isPngSequence) {
|
|
6324
6313
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
6325
|
-
if (!existsSync9(outputPath))
|
|
6314
|
+
if (!existsSync9(outputPath)) mkdirSync4(outputPath, { recursive: true });
|
|
6326
6315
|
const captured = readdirSync2(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
6327
6316
|
if (captured.length === 0) {
|
|
6328
6317
|
throw new Error(
|
|
@@ -6778,7 +6767,7 @@ import {
|
|
|
6778
6767
|
// src/services/distributed/shared.ts
|
|
6779
6768
|
import { execFile as execFileCallback } from "node:child_process";
|
|
6780
6769
|
import { dirname as dirname8, join as join14 } from "node:path";
|
|
6781
|
-
import { existsSync as existsSync11, readFileSync as
|
|
6770
|
+
import { existsSync as existsSync11, readFileSync as readFileSync6 } from "node:fs";
|
|
6782
6771
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6783
6772
|
import { promisify as promisify2 } from "node:util";
|
|
6784
6773
|
import {
|
|
@@ -7056,7 +7045,7 @@ function readProducerVersion() {
|
|
|
7056
7045
|
const candidate = join14(current, "package.json");
|
|
7057
7046
|
if (existsSync11(candidate)) {
|
|
7058
7047
|
try {
|
|
7059
|
-
const pkg = JSON.parse(
|
|
7048
|
+
const pkg = JSON.parse(readFileSync6(candidate, "utf-8"));
|
|
7060
7049
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
7061
7050
|
cachedProducerVersion = pkg.version;
|
|
7062
7051
|
return pkg.version;
|
|
@@ -7309,8 +7298,72 @@ async function measurePlanSecondsPerFrame(input) {
|
|
|
7309
7298
|
}
|
|
7310
7299
|
}
|
|
7311
7300
|
|
|
7301
|
+
// src/services/distributed/chunkWeights.ts
|
|
7302
|
+
import { resolveFrameFormat } from "@hyperframes/engine";
|
|
7303
|
+
var PAGE_BASE_WEIGHT = 0.7;
|
|
7304
|
+
var PNG_WEIGHT_FACTOR = 1.5;
|
|
7305
|
+
function isUniformFrameCost(weights) {
|
|
7306
|
+
let lightest = Infinity;
|
|
7307
|
+
let heaviest = 0;
|
|
7308
|
+
for (const weight of weights) {
|
|
7309
|
+
lightest = Math.min(lightest, weight);
|
|
7310
|
+
heaviest = Math.max(heaviest, weight);
|
|
7311
|
+
}
|
|
7312
|
+
return heaviest <= lightest * 1.05;
|
|
7313
|
+
}
|
|
7314
|
+
function planFrameWeights(input) {
|
|
7315
|
+
const { totalFrames, fps } = input;
|
|
7316
|
+
const delta = new Float64Array(totalFrames + 1);
|
|
7317
|
+
const outputPixels = input.width * input.height;
|
|
7318
|
+
const extractedById = new Map(input.extracted.map((entry) => [entry.videoId, entry]));
|
|
7319
|
+
for (const video of input.videos) {
|
|
7320
|
+
const entry = extractedById.get(video.id);
|
|
7321
|
+
if (!entry || video.hidden) continue;
|
|
7322
|
+
const start = Math.max(0, Math.round(video.start * fps));
|
|
7323
|
+
const end = Math.min(totalFrames, Math.round(video.end * fps));
|
|
7324
|
+
if (!(end > start)) continue;
|
|
7325
|
+
const { width, height } = entry.metadata;
|
|
7326
|
+
const factor = resolveFrameFormat(entry.metadata) === "png" ? PNG_WEIGHT_FACTOR : 1;
|
|
7327
|
+
const weight = Math.min(width * height, outputPixels) / 1e6 * factor;
|
|
7328
|
+
delta[start] = delta[start] + weight;
|
|
7329
|
+
delta[end] = delta[end] - weight;
|
|
7330
|
+
}
|
|
7331
|
+
const weights = new Float64Array(totalFrames);
|
|
7332
|
+
let active = 0;
|
|
7333
|
+
for (let frame = 0; frame < totalFrames; frame++) {
|
|
7334
|
+
active += delta[frame];
|
|
7335
|
+
weights[frame] = PAGE_BASE_WEIGHT + active;
|
|
7336
|
+
}
|
|
7337
|
+
return weights;
|
|
7338
|
+
}
|
|
7339
|
+
function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
7340
|
+
if (isUniformFrameCost(weights)) return null;
|
|
7341
|
+
const totalFrames = weights.length;
|
|
7342
|
+
const total = weights.reduce((sum2, weight) => sum2 + weight, 0);
|
|
7343
|
+
const minSize = Math.max(1, Math.min(minFrames, Math.floor(totalFrames / chunkCount)));
|
|
7344
|
+
const maxSize = Math.max(maxFrames, Math.ceil(totalFrames / chunkCount));
|
|
7345
|
+
const slices = [];
|
|
7346
|
+
let startFrame = 0;
|
|
7347
|
+
let frame = 0;
|
|
7348
|
+
let costBefore = 0;
|
|
7349
|
+
for (let index = 0; index < chunkCount - 1; index++) {
|
|
7350
|
+
const target = (index + 1) * total / chunkCount;
|
|
7351
|
+
while (frame < totalFrames && costBefore + weights[frame] / 2 < target) {
|
|
7352
|
+
costBefore += weights[frame++];
|
|
7353
|
+
}
|
|
7354
|
+
const remaining = chunkCount - index - 1;
|
|
7355
|
+
const low = Math.max(startFrame + minSize, totalFrames - remaining * maxSize);
|
|
7356
|
+
const high = Math.min(startFrame + maxSize, totalFrames - remaining * minSize);
|
|
7357
|
+
const endFrame = Math.min(high, Math.max(low, frame));
|
|
7358
|
+
slices.push({ index, startFrame, endFrame });
|
|
7359
|
+
startFrame = endFrame;
|
|
7360
|
+
}
|
|
7361
|
+
slices.push({ index: chunkCount - 1, startFrame, endFrame: totalFrames });
|
|
7362
|
+
return slices;
|
|
7363
|
+
}
|
|
7364
|
+
|
|
7312
7365
|
// src/services/render/stages/freezePlan.ts
|
|
7313
|
-
import { existsSync as existsSync13, mkdirSync as
|
|
7366
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7314
7367
|
import { join as join17, relative as relative3, resolve as resolve7 } from "node:path";
|
|
7315
7368
|
|
|
7316
7369
|
// src/services/distributed/planProtocol.ts
|
|
@@ -7410,7 +7463,7 @@ function readPlanProtocolV1(planJson, capabilities = DISTRIBUTED_RENDER_CAPABILI
|
|
|
7410
7463
|
|
|
7411
7464
|
// src/services/render/stages/planHash.ts
|
|
7412
7465
|
import { createHash as createHash4 } from "node:crypto";
|
|
7413
|
-
import { closeSync as
|
|
7466
|
+
import { closeSync as closeSync2, createReadStream as createReadStream3, openSync as openSync2, readSync } from "node:fs";
|
|
7414
7467
|
var PLAN_HASH_SCHEMA_PREFIX = "hyperframes-plan-hash-v1\0";
|
|
7415
7468
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
7416
7469
|
function computePlanHash(input) {
|
|
@@ -7468,7 +7521,7 @@ function sha256Hex(bytes) {
|
|
|
7468
7521
|
function sha256File(path) {
|
|
7469
7522
|
const hash = createHash4("sha256");
|
|
7470
7523
|
const buffer = Buffer.allocUnsafe(1024 * 1024);
|
|
7471
|
-
const fd =
|
|
7524
|
+
const fd = openSync2(path, "r");
|
|
7472
7525
|
try {
|
|
7473
7526
|
let bytesRead = 0;
|
|
7474
7527
|
do {
|
|
@@ -7476,7 +7529,7 @@ function sha256File(path) {
|
|
|
7476
7529
|
if (bytesRead > 0) hash.update(buffer.subarray(0, bytesRead));
|
|
7477
7530
|
} while (bytesRead > 0);
|
|
7478
7531
|
} finally {
|
|
7479
|
-
|
|
7532
|
+
closeSync2(fd);
|
|
7480
7533
|
}
|
|
7481
7534
|
return hash.digest("hex");
|
|
7482
7535
|
}
|
|
@@ -7550,7 +7603,7 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
7550
7603
|
const assets = [];
|
|
7551
7604
|
for (const file of files) {
|
|
7552
7605
|
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
7553
|
-
compositionHtml =
|
|
7606
|
+
compositionHtml = readFileSync7(file.absolutePath);
|
|
7554
7607
|
continue;
|
|
7555
7608
|
}
|
|
7556
7609
|
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
@@ -7574,7 +7627,7 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
7574
7627
|
}
|
|
7575
7628
|
function readPlanRemoteFiles(planDir) {
|
|
7576
7629
|
const path = join17(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
7577
|
-
return existsSync13(path) ? JSON.parse(
|
|
7630
|
+
return existsSync13(path) ? JSON.parse(readFileSync7(path, "utf-8")) : [];
|
|
7578
7631
|
}
|
|
7579
7632
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
7580
7633
|
const planJsonPath = join17(planDir, "plan.json");
|
|
@@ -7585,8 +7638,8 @@ function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
|
7585
7638
|
if (!existsSync13(encoderJsonPath)) {
|
|
7586
7639
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
7587
7640
|
}
|
|
7588
|
-
const planJson = JSON.parse(
|
|
7589
|
-
const encoderConfigCanonicalJson =
|
|
7641
|
+
const planJson = JSON.parse(readFileSync7(planJsonPath, "utf-8"));
|
|
7642
|
+
const encoderConfigCanonicalJson = readFileSync7(encoderJsonPath, "utf-8");
|
|
7590
7643
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
7591
7644
|
return computePlanHash({
|
|
7592
7645
|
compositionHtml,
|
|
@@ -7616,7 +7669,7 @@ async function freezePlan(input) {
|
|
|
7616
7669
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
7617
7670
|
}
|
|
7618
7671
|
const metaDir = join17(planDir, "meta");
|
|
7619
|
-
if (!existsSync13(metaDir))
|
|
7672
|
+
if (!existsSync13(metaDir)) mkdirSync6(metaDir, { recursive: true });
|
|
7620
7673
|
writeFileSync4(
|
|
7621
7674
|
join17(metaDir, "composition.json"),
|
|
7622
7675
|
`${JSON.stringify(composition, null, 2)}
|
|
@@ -7692,13 +7745,13 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
7692
7745
|
}
|
|
7693
7746
|
|
|
7694
7747
|
// src/services/distributed/chunkAudio.ts
|
|
7695
|
-
import { closeSync as
|
|
7748
|
+
import { closeSync as closeSync3, existsSync as existsSync14, ftruncateSync, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync8 } from "node:fs";
|
|
7696
7749
|
import { writeFileSync as writeFileSync5, writeSync } from "node:fs";
|
|
7697
7750
|
import { join as join19 } from "node:path";
|
|
7698
7751
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
|
|
7699
7752
|
|
|
7700
7753
|
// src/services/distributed/mp4Stitch.ts
|
|
7701
|
-
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as
|
|
7754
|
+
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync2, rmSync as rmSync7 } from "node:fs";
|
|
7702
7755
|
import { open } from "node:fs/promises";
|
|
7703
7756
|
import { renameWithRetry as renameWithRetry5 } from "@hyperframes/engine";
|
|
7704
7757
|
import { dirname as dirname9, join as join18 } from "node:path";
|
|
@@ -8065,8 +8118,8 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
8065
8118
|
const sourcePaths = audioPath ? [audioPath, ...chunkPaths] : chunkPaths;
|
|
8066
8119
|
const layouts = await Promise.all(sourcePaths.map((path) => readMp4Layout(fileRange(path))));
|
|
8067
8120
|
const header = audioPath ? buildStitchedHeader(layouts.slice(1), layouts[0]) : buildStitchedHeader(layouts);
|
|
8068
|
-
|
|
8069
|
-
const workDir =
|
|
8121
|
+
mkdirSync7(dirname9(outputPath), { recursive: true });
|
|
8122
|
+
const workDir = mkdtempSync2(`${outputPath}.stitch-`);
|
|
8070
8123
|
const out = createWriteStream(join18(workDir, "out.mp4"));
|
|
8071
8124
|
try {
|
|
8072
8125
|
out.write(header);
|
|
@@ -8076,7 +8129,7 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
8076
8129
|
renameWithRetry5(join18(workDir, "out.mp4"), outputPath);
|
|
8077
8130
|
} finally {
|
|
8078
8131
|
out.destroy();
|
|
8079
|
-
|
|
8132
|
+
rmSync7(workDir, { recursive: true, force: true });
|
|
8080
8133
|
}
|
|
8081
8134
|
}
|
|
8082
8135
|
|
|
@@ -8165,9 +8218,9 @@ async function writeChunkAudioSlices(input) {
|
|
|
8165
8218
|
offset += track.sizes[offsets.length - 1];
|
|
8166
8219
|
}
|
|
8167
8220
|
});
|
|
8168
|
-
const bytes =
|
|
8221
|
+
const bytes = readFileSync8(input.audioPath);
|
|
8169
8222
|
const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
|
|
8170
|
-
|
|
8223
|
+
mkdirSync8(join19(input.planDir, SLICE_DIR), { recursive: true });
|
|
8171
8224
|
input.chunks.forEach((_, index) => {
|
|
8172
8225
|
const cut = audioCutForChunk(grid, input.chunks, index, input.fps);
|
|
8173
8226
|
if (!cut) return;
|
|
@@ -8184,9 +8237,9 @@ async function writeChunkAudioSlices(input) {
|
|
|
8184
8237
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
8185
8238
|
const layoutPath = join19(planDir, SLICE_LAYOUT);
|
|
8186
8239
|
if (!existsSync14(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
8187
|
-
const layout = JSON.parse(
|
|
8188
|
-
const frame =
|
|
8189
|
-
const fd =
|
|
8240
|
+
const layout = JSON.parse(readFileSync8(layoutPath, "utf-8"));
|
|
8241
|
+
const frame = readFileSync8(join19(planDir, SLICE_FRAME));
|
|
8242
|
+
const fd = openSync3(scratchPath, "w");
|
|
8190
8243
|
try {
|
|
8191
8244
|
writeSync(fd, frame, 0, layout.payloadStart, 0);
|
|
8192
8245
|
writeSync(
|
|
@@ -8198,12 +8251,12 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
|
8198
8251
|
);
|
|
8199
8252
|
const start = layout.partStarts[chunkIndex];
|
|
8200
8253
|
if (start !== void 0) {
|
|
8201
|
-
const part =
|
|
8254
|
+
const part = readFileSync8(join19(planDir, chunkAudioSlicePath(chunkIndex)));
|
|
8202
8255
|
writeSync(fd, part, 0, part.length, start);
|
|
8203
8256
|
}
|
|
8204
8257
|
ftruncateSync(fd, layout.sizeBytes);
|
|
8205
8258
|
} finally {
|
|
8206
|
-
|
|
8259
|
+
closeSync3(fd);
|
|
8207
8260
|
}
|
|
8208
8261
|
return scratchPath;
|
|
8209
8262
|
}
|
|
@@ -8424,6 +8477,7 @@ var PLAN_PROJECT_DIR_SKIP_SEGMENTS = /* @__PURE__ */ new Set([
|
|
|
8424
8477
|
var DEFAULT_CHUNK_SIZE = 240;
|
|
8425
8478
|
var DEFAULT_MAX_PARALLEL_CHUNKS = 16;
|
|
8426
8479
|
var MIN_CHUNK_SIZE = 10;
|
|
8480
|
+
var MAX_CHUNK_SIZE = 3600;
|
|
8427
8481
|
var MAX_PARALLEL_CHUNKS_CEILING = 256;
|
|
8428
8482
|
var ESTIMATED_SECONDS_PER_MEGAPIXEL_FRAME = 0.11;
|
|
8429
8483
|
var ESTIMATED_CHUNK_OVERHEAD_SECONDS = 60;
|
|
@@ -8450,6 +8504,11 @@ var ChunkExceedsWorkerTimeoutError = class extends Error {
|
|
|
8450
8504
|
this.name = "ChunkExceedsWorkerTimeoutError";
|
|
8451
8505
|
}
|
|
8452
8506
|
};
|
|
8507
|
+
function framesWithinWorkerTimeout(workerTimeoutSeconds, secondsPerFrame) {
|
|
8508
|
+
return Math.floor(
|
|
8509
|
+
(workerTimeoutSeconds * WORKER_TIMEOUT_MARGIN - ESTIMATED_CHUNK_OVERHEAD_SECONDS) / secondsPerFrame
|
|
8510
|
+
);
|
|
8511
|
+
}
|
|
8453
8512
|
function resolveMaxParallelChunks(config, totalFrames, megapixels, log = defaultLogger, measuredSecondsPerFrame) {
|
|
8454
8513
|
if (config.maxParallelChunks !== void 0) return config.maxParallelChunks;
|
|
8455
8514
|
const { workerTimeoutSeconds, workerConcurrency } = config;
|
|
@@ -8462,9 +8521,7 @@ function resolveMaxParallelChunks(config, totalFrames, megapixels, log = default
|
|
|
8462
8521
|
const minFramesPerChunk = Math.ceil(ESTIMATED_CHUNK_OVERHEAD_SECONDS / secondsPerFrame);
|
|
8463
8522
|
const wide = Math.max(1, Math.min(wave, Math.floor(totalFrames / minFramesPerChunk)));
|
|
8464
8523
|
if (workerTimeoutSeconds === void 0) return wide;
|
|
8465
|
-
const maxFramesPerChunk =
|
|
8466
|
-
(workerTimeoutSeconds * WORKER_TIMEOUT_MARGIN - ESTIMATED_CHUNK_OVERHEAD_SECONDS) / secondsPerFrame
|
|
8467
|
-
);
|
|
8524
|
+
const maxFramesPerChunk = framesWithinWorkerTimeout(workerTimeoutSeconds, secondsPerFrame);
|
|
8468
8525
|
const needed = maxFramesPerChunk < 1 ? Infinity : Math.ceil(totalFrames / maxFramesPerChunk);
|
|
8469
8526
|
if (needed > MAX_PARALLEL_CHUNKS_CEILING) {
|
|
8470
8527
|
const framesAtCeiling = Math.ceil(totalFrames / MAX_PARALLEL_CHUNKS_CEILING);
|
|
@@ -8584,7 +8641,7 @@ var FREEZE_OWNED_PLAN_FILES = [
|
|
|
8584
8641
|
];
|
|
8585
8642
|
function removeFreezeOwnedPlanFiles(planDir) {
|
|
8586
8643
|
for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
|
|
8587
|
-
|
|
8644
|
+
rmSync8(join21(planDir, relativePath), { force: true });
|
|
8588
8645
|
}
|
|
8589
8646
|
}
|
|
8590
8647
|
function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
@@ -8596,6 +8653,15 @@ function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
|
8596
8653
|
}
|
|
8597
8654
|
return slices;
|
|
8598
8655
|
}
|
|
8656
|
+
function buildPlanChunkSlices(frameWeights, config, chunkCount, effectiveChunkSize, maxFramesPerChunk = Infinity) {
|
|
8657
|
+
const weighted = config.chunkSize === void 0 ? buildWeightedChunkSlices(
|
|
8658
|
+
frameWeights,
|
|
8659
|
+
chunkCount,
|
|
8660
|
+
MIN_CHUNK_SIZE,
|
|
8661
|
+
Math.min(config.targetChunkFrames ?? MAX_CHUNK_SIZE, MAX_CHUNK_SIZE, maxFramesPerChunk)
|
|
8662
|
+
) : null;
|
|
8663
|
+
return weighted ?? buildChunkSlices(frameWeights.length, chunkCount, effectiveChunkSize);
|
|
8664
|
+
}
|
|
8599
8665
|
async function readFontSnapshotSha() {
|
|
8600
8666
|
const module = await import("@hyperframes/core/fonts/embedded-data");
|
|
8601
8667
|
const data = module.EMBEDDED_FONT_DATA;
|
|
@@ -8729,7 +8795,7 @@ function createVideoSourceShipper(input) {
|
|
|
8729
8795
|
if (result === null) {
|
|
8730
8796
|
result = `video-sources/${shipped.size}${extname4(current)}`;
|
|
8731
8797
|
const destination = join21(input.planDir, ...result.split("/"));
|
|
8732
|
-
|
|
8798
|
+
mkdirSync9(join21(input.planDir, "video-sources"), { recursive: true });
|
|
8733
8799
|
try {
|
|
8734
8800
|
linkSync(current, destination);
|
|
8735
8801
|
} catch {
|
|
@@ -8749,7 +8815,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8749
8815
|
useGpu: false,
|
|
8750
8816
|
browserGpuMode: "software"
|
|
8751
8817
|
});
|
|
8752
|
-
if (!existsSync15(planDir))
|
|
8818
|
+
if (!existsSync15(planDir)) mkdirSync9(planDir, { recursive: true });
|
|
8753
8819
|
const log = config.logger ?? defaultLogger;
|
|
8754
8820
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
8755
8821
|
const abortSignal = config.abortSignal;
|
|
@@ -8783,10 +8849,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
8783
8849
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
8784
8850
|
}
|
|
8785
8851
|
const workDir = join21(planDir, ".plan-work");
|
|
8786
|
-
|
|
8787
|
-
|
|
8852
|
+
rmSync8(workDir, { recursive: true, force: true });
|
|
8853
|
+
mkdirSync9(workDir, { recursive: true });
|
|
8788
8854
|
const compiledDir = join21(workDir, "compiled");
|
|
8789
|
-
|
|
8855
|
+
mkdirSync9(compiledDir, { recursive: true });
|
|
8790
8856
|
let stageStarted = Date.now();
|
|
8791
8857
|
cpSync2(projectDir, compiledDir, {
|
|
8792
8858
|
recursive: true,
|
|
@@ -8990,15 +9056,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
8990
9056
|
logPlanStage(log, "extract", stageStarted);
|
|
8991
9057
|
const stagedVideoFrames = join21(compiledDir, "__hyperframes_video_frames");
|
|
8992
9058
|
const videoFramesDst = join21(planDir, "video-frames");
|
|
8993
|
-
if (existsSync15(videoFramesDst))
|
|
9059
|
+
if (existsSync15(videoFramesDst)) rmSync8(videoFramesDst, { recursive: true, force: true });
|
|
8994
9060
|
const videoSourcesDir = join21(planDir, "video-sources");
|
|
8995
|
-
|
|
9061
|
+
rmSync8(videoSourcesDir, { recursive: true, force: true });
|
|
8996
9062
|
if (existsSync15(stagedVideoFrames)) {
|
|
8997
9063
|
renameWithRetry6(stagedVideoFrames, videoFramesDst);
|
|
8998
9064
|
} else {
|
|
8999
|
-
|
|
9065
|
+
mkdirSync9(videoFramesDst, { recursive: true });
|
|
9000
9066
|
}
|
|
9001
|
-
if (existsSync15(finalCompiledDir))
|
|
9067
|
+
if (existsSync15(finalCompiledDir)) rmSync8(finalCompiledDir, { recursive: true, force: true });
|
|
9002
9068
|
renameWithRetry6(compiledDir, finalCompiledDir);
|
|
9003
9069
|
const shipVideoSource = createVideoSourceShipper({
|
|
9004
9070
|
planDir,
|
|
@@ -9028,7 +9094,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9028
9094
|
};
|
|
9029
9095
|
})
|
|
9030
9096
|
});
|
|
9031
|
-
|
|
9097
|
+
mkdirSync9(join21(planDir, "meta"), { recursive: true });
|
|
9032
9098
|
writePlanRemoteFiles(
|
|
9033
9099
|
planDir,
|
|
9034
9100
|
remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
|
|
@@ -9045,6 +9111,14 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9045
9111
|
const digests = options.digests ?? /* @__PURE__ */ new Map();
|
|
9046
9112
|
const separateAudio = plansSeparateAudio(config, options);
|
|
9047
9113
|
const chunksCarryAudio = !separateAudio && carriesChunkAudio(config, options);
|
|
9114
|
+
const frameWeights = planFrameWeights({
|
|
9115
|
+
videos: planVideosJson.videos,
|
|
9116
|
+
extracted: planVideosJson.extracted,
|
|
9117
|
+
totalFrames,
|
|
9118
|
+
fps: config.fps,
|
|
9119
|
+
width,
|
|
9120
|
+
height
|
|
9121
|
+
});
|
|
9048
9122
|
stageStarted = Date.now();
|
|
9049
9123
|
const [audioOutcome, overlapOutcome, frameCostOutcome] = await Promise.allSettled([
|
|
9050
9124
|
separateAudio ? null : mixPlanAudio(c, finalCompiledDir).finally(() => logPlanStage(log, "audio", stageStarted)),
|
|
@@ -9095,7 +9169,16 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9095
9169
|
maxParallel,
|
|
9096
9170
|
config.targetChunkFrames
|
|
9097
9171
|
);
|
|
9098
|
-
const chunks =
|
|
9172
|
+
const chunks = buildPlanChunkSlices(
|
|
9173
|
+
frameWeights,
|
|
9174
|
+
config,
|
|
9175
|
+
chunkCount,
|
|
9176
|
+
effectiveChunkSize,
|
|
9177
|
+
chunkSizingUsesFrameCost(config) && config.workerTimeoutSeconds !== void 0 ? framesWithinWorkerTimeout(
|
|
9178
|
+
config.workerTimeoutSeconds,
|
|
9179
|
+
estimateFrameSeconds(megapixels, frameCostOutcome.value)
|
|
9180
|
+
) : void 0
|
|
9181
|
+
);
|
|
9099
9182
|
const audioResult = audioOutcome.value;
|
|
9100
9183
|
if (audioResult?.audioError) {
|
|
9101
9184
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
@@ -9149,7 +9232,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9149
9232
|
format: config.format
|
|
9150
9233
|
};
|
|
9151
9234
|
try {
|
|
9152
|
-
|
|
9235
|
+
rmSync8(workDir, { recursive: true, force: true });
|
|
9153
9236
|
} catch (err) {
|
|
9154
9237
|
log.warn("[plan] failed to remove temp work dir", {
|
|
9155
9238
|
workDir,
|
|
@@ -9219,7 +9302,7 @@ async function buildPlanAudio(projectDir, config, workRoot, outputPath, options
|
|
|
9219
9302
|
renameWithRetry6(mixed.audioOutputPath, outputPath);
|
|
9220
9303
|
return { hasAudio: true };
|
|
9221
9304
|
} finally {
|
|
9222
|
-
|
|
9305
|
+
rmSync8(c.workDir, { recursive: true, force: true });
|
|
9223
9306
|
}
|
|
9224
9307
|
}
|
|
9225
9308
|
async function plan(projectDir, config, planDir, options = {}) {
|
|
@@ -9244,11 +9327,11 @@ import {
|
|
|
9244
9327
|
copyFileSync as copyFileSync7,
|
|
9245
9328
|
existsSync as existsSync17,
|
|
9246
9329
|
lstatSync as lstatSync2,
|
|
9247
|
-
mkdirSync as
|
|
9248
|
-
mkdtempSync as
|
|
9249
|
-
readFileSync as
|
|
9330
|
+
mkdirSync as mkdirSync11,
|
|
9331
|
+
mkdtempSync as mkdtempSync4,
|
|
9332
|
+
readFileSync as readFileSync9,
|
|
9250
9333
|
readdirSync as readdirSync7,
|
|
9251
|
-
rmSync as
|
|
9334
|
+
rmSync as rmSync10,
|
|
9252
9335
|
statSync as statSync6,
|
|
9253
9336
|
writeFileSync as writeFileSync8
|
|
9254
9337
|
} from "node:fs";
|
|
@@ -9262,9 +9345,9 @@ import {
|
|
|
9262
9345
|
copyFileSync as copyFileSync6,
|
|
9263
9346
|
existsSync as existsSync16,
|
|
9264
9347
|
linkSync as linkSync2,
|
|
9265
|
-
mkdirSync as
|
|
9266
|
-
mkdtempSync as
|
|
9267
|
-
rmSync as
|
|
9348
|
+
mkdirSync as mkdirSync10,
|
|
9349
|
+
mkdtempSync as mkdtempSync3,
|
|
9350
|
+
rmSync as rmSync9,
|
|
9268
9351
|
statSync as statSync5,
|
|
9269
9352
|
writeFileSync as writeFileSync7
|
|
9270
9353
|
} from "node:fs";
|
|
@@ -9334,8 +9417,8 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9334
9417
|
}
|
|
9335
9418
|
this.destinationDir = destinationDir;
|
|
9336
9419
|
this.#linkFile = options.linkFile ?? linkSync2;
|
|
9337
|
-
|
|
9338
|
-
this.temporaryDir =
|
|
9420
|
+
mkdirSync10(dirname10(destinationDir), { recursive: true });
|
|
9421
|
+
this.temporaryDir = mkdtempSync3(join23(dirname10(destinationDir), ".plan-v2-publish-"));
|
|
9339
9422
|
}
|
|
9340
9423
|
async putBlob(blob) {
|
|
9341
9424
|
const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
|
|
@@ -9347,8 +9430,8 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9347
9430
|
}
|
|
9348
9431
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
9349
9432
|
if (existsSync16(destinationPath)) return;
|
|
9350
|
-
|
|
9351
|
-
const stagingDir =
|
|
9433
|
+
mkdirSync10(dirname10(destinationPath), { recursive: true });
|
|
9434
|
+
const stagingDir = mkdtempSync3(join23(dirname10(destinationPath), ".plan-v2-blob-"));
|
|
9352
9435
|
const temporaryPath = join23(stagingDir, "blob");
|
|
9353
9436
|
try {
|
|
9354
9437
|
try {
|
|
@@ -9359,7 +9442,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9359
9442
|
}
|
|
9360
9443
|
renameWithRetry7(temporaryPath, destinationPath);
|
|
9361
9444
|
} finally {
|
|
9362
|
-
|
|
9445
|
+
rmSync9(stagingDir, { recursive: true, force: true });
|
|
9363
9446
|
}
|
|
9364
9447
|
}
|
|
9365
9448
|
async commitManifest(manifestBytes) {
|
|
@@ -9376,7 +9459,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
9376
9459
|
}
|
|
9377
9460
|
async abort() {
|
|
9378
9461
|
if (!this.#committed) {
|
|
9379
|
-
|
|
9462
|
+
rmSync9(this.temporaryDir, { recursive: true, force: true });
|
|
9380
9463
|
}
|
|
9381
9464
|
}
|
|
9382
9465
|
};
|
|
@@ -9395,7 +9478,7 @@ function isNonNegativeIntegerArray(value) {
|
|
|
9395
9478
|
return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
|
|
9396
9479
|
}
|
|
9397
9480
|
function readJsonFile(path, label) {
|
|
9398
|
-
const contents =
|
|
9481
|
+
const contents = readFileSync9(path, "utf-8");
|
|
9399
9482
|
try {
|
|
9400
9483
|
const parsed = JSON.parse(contents);
|
|
9401
9484
|
return parsed;
|
|
@@ -9547,7 +9630,7 @@ function materializeExtractedVideoDirectories(planDir) {
|
|
|
9547
9630
|
if (!existsSync17(videosPath)) return;
|
|
9548
9631
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
9549
9632
|
for (const video of videos.extracted) {
|
|
9550
|
-
|
|
9633
|
+
mkdirSync11(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
9551
9634
|
}
|
|
9552
9635
|
}
|
|
9553
9636
|
function parseChunkSlices(value) {
|
|
@@ -9633,14 +9716,14 @@ function computeManifestHash(manifest) {
|
|
|
9633
9716
|
}
|
|
9634
9717
|
function writeBlob(sourcePath, destinationPath) {
|
|
9635
9718
|
if (existsSync17(destinationPath)) return;
|
|
9636
|
-
|
|
9637
|
-
const temporaryDir =
|
|
9719
|
+
mkdirSync11(dirname11(destinationPath), { recursive: true });
|
|
9720
|
+
const temporaryDir = mkdtempSync4(join24(dirname11(destinationPath), ".plan-v2-blob-"));
|
|
9638
9721
|
const temporaryPath = join24(temporaryDir, "blob");
|
|
9639
9722
|
try {
|
|
9640
9723
|
copyFileSync7(sourcePath, temporaryPath);
|
|
9641
9724
|
renameWithRetry8(temporaryPath, destinationPath);
|
|
9642
9725
|
} finally {
|
|
9643
|
-
|
|
9726
|
+
rmSync10(temporaryDir, { recursive: true, force: true });
|
|
9644
9727
|
}
|
|
9645
9728
|
}
|
|
9646
9729
|
function publishBlobFor(absolutePath, digests) {
|
|
@@ -9759,8 +9842,8 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
9759
9842
|
if (publication.storedBlobs.length > 0) {
|
|
9760
9843
|
throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
|
|
9761
9844
|
}
|
|
9762
|
-
|
|
9763
|
-
const tempDir =
|
|
9845
|
+
mkdirSync11(dirname11(planV2Dir), { recursive: true });
|
|
9846
|
+
const tempDir = mkdtempSync4(join24(dirname11(planV2Dir), ".plan-v2-build-"));
|
|
9764
9847
|
try {
|
|
9765
9848
|
for (const blob of publication.blobs) {
|
|
9766
9849
|
writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
|
|
@@ -9773,7 +9856,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
9773
9856
|
renameWithRetry8(tempDir, planV2Dir);
|
|
9774
9857
|
return resultFromManifest(planV2Dir, publication.manifest);
|
|
9775
9858
|
} catch (error) {
|
|
9776
|
-
|
|
9859
|
+
rmSync10(tempDir, { recursive: true, force: true });
|
|
9777
9860
|
throw error;
|
|
9778
9861
|
}
|
|
9779
9862
|
}
|
|
@@ -9813,7 +9896,7 @@ async function publishPlanV2FromV1(executionPlanDir, publisher) {
|
|
|
9813
9896
|
return publishPlanV2FromExecutionPlan(executionPlanDir, publisher);
|
|
9814
9897
|
}
|
|
9815
9898
|
async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
|
|
9816
|
-
const stagingRoot =
|
|
9899
|
+
const stagingRoot = mkdtempSync4(join24(options.stagingParentDir ?? tmpdir(), ".execution-plan-"));
|
|
9817
9900
|
const digests = /* @__PURE__ */ new Map();
|
|
9818
9901
|
try {
|
|
9819
9902
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
@@ -9840,7 +9923,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
9840
9923
|
}
|
|
9841
9924
|
throw error;
|
|
9842
9925
|
} finally {
|
|
9843
|
-
|
|
9926
|
+
rmSync10(stagingRoot, { recursive: true, force: true });
|
|
9844
9927
|
}
|
|
9845
9928
|
}
|
|
9846
9929
|
async function planV2(projectDir, config, planV2Dir) {
|
|
@@ -10037,12 +10120,12 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
10037
10120
|
artifact,
|
|
10038
10121
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
10039
10122
|
}));
|
|
10040
|
-
|
|
10041
|
-
const tempDir =
|
|
10123
|
+
mkdirSync11(dirname11(destinationDir), { recursive: true });
|
|
10124
|
+
const tempDir = mkdtempSync4(join24(dirname11(destinationDir), ".plan-v2-materialize-"));
|
|
10042
10125
|
try {
|
|
10043
10126
|
for (const { artifact, sourcePath } of verified) {
|
|
10044
10127
|
const destinationPath = join24(tempDir, ...artifact.path.split("/"));
|
|
10045
|
-
|
|
10128
|
+
mkdirSync11(dirname11(destinationPath), { recursive: true });
|
|
10046
10129
|
copyFileSync7(sourcePath, destinationPath);
|
|
10047
10130
|
}
|
|
10048
10131
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
@@ -10057,7 +10140,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
10057
10140
|
);
|
|
10058
10141
|
renameWithRetry8(tempDir, destinationDir);
|
|
10059
10142
|
} catch (error) {
|
|
10060
|
-
|
|
10143
|
+
rmSync10(tempDir, { recursive: true, force: true });
|
|
10061
10144
|
throw error;
|
|
10062
10145
|
}
|
|
10063
10146
|
return {
|
|
@@ -10121,7 +10204,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
10121
10204
|
}
|
|
10122
10205
|
|
|
10123
10206
|
// src/services/distributed/planV2Execution.ts
|
|
10124
|
-
import { mkdtempSync as
|
|
10207
|
+
import { mkdtempSync as mkdtempSync6, rmSync as rmSync13 } from "node:fs";
|
|
10125
10208
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10126
10209
|
import { join as join27 } from "node:path";
|
|
10127
10210
|
|
|
@@ -10129,11 +10212,11 @@ import { join as join27 } from "node:path";
|
|
|
10129
10212
|
import {
|
|
10130
10213
|
cpSync as cpSync3,
|
|
10131
10214
|
existsSync as existsSync18,
|
|
10132
|
-
mkdirSync as
|
|
10133
|
-
mkdtempSync as
|
|
10134
|
-
readFileSync as
|
|
10215
|
+
mkdirSync as mkdirSync12,
|
|
10216
|
+
mkdtempSync as mkdtempSync5,
|
|
10217
|
+
readFileSync as readFileSync10,
|
|
10135
10218
|
readdirSync as readdirSync8,
|
|
10136
|
-
rmSync as
|
|
10219
|
+
rmSync as rmSync11,
|
|
10137
10220
|
statSync as statSync7,
|
|
10138
10221
|
writeFileSync as writeFileSync9
|
|
10139
10222
|
} from "node:fs";
|
|
@@ -10179,9 +10262,9 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
10179
10262
|
const format = plan2.dimensions.format;
|
|
10180
10263
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
10181
10264
|
if (!existsSync18(dirname12(outputPath))) {
|
|
10182
|
-
|
|
10265
|
+
mkdirSync12(dirname12(outputPath), { recursive: true });
|
|
10183
10266
|
}
|
|
10184
|
-
const workDir =
|
|
10267
|
+
const workDir = mkdtempSync5(`${outputPath}.assemble-work-`);
|
|
10185
10268
|
const fps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
|
|
10186
10269
|
const stageMs = {};
|
|
10187
10270
|
const ctx = {
|
|
@@ -10212,7 +10295,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
10212
10295
|
});
|
|
10213
10296
|
} finally {
|
|
10214
10297
|
try {
|
|
10215
|
-
|
|
10298
|
+
rmSync11(workDir, { recursive: true, force: true });
|
|
10216
10299
|
} catch (err) {
|
|
10217
10300
|
log.warn("[assemble] failed to remove work dir", {
|
|
10218
10301
|
workDir,
|
|
@@ -10234,13 +10317,13 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
10234
10317
|
if (!existsSync18(planJsonPath)) {
|
|
10235
10318
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
10236
10319
|
}
|
|
10237
|
-
const plan2 = JSON.parse(
|
|
10320
|
+
const plan2 = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
10238
10321
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
10239
10322
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
10240
10323
|
if (!existsSync18(chunksJsonPath)) {
|
|
10241
10324
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
10242
10325
|
}
|
|
10243
|
-
const chunks = JSON.parse(
|
|
10326
|
+
const chunks = JSON.parse(readFileSync10(chunksJsonPath, "utf-8"));
|
|
10244
10327
|
if (chunkPaths.length !== chunks.length) {
|
|
10245
10328
|
throw new Error(
|
|
10246
10329
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
@@ -10383,13 +10466,13 @@ function stitchesSeparateAudio(planDir) {
|
|
|
10383
10466
|
return lockedAudio(planDir).separateAudio === true;
|
|
10384
10467
|
}
|
|
10385
10468
|
function planFormat(planDir) {
|
|
10386
|
-
const plan2 = JSON.parse(
|
|
10469
|
+
const plan2 = JSON.parse(readFileSync10(join25(planDir, "plan.json"), "utf-8"));
|
|
10387
10470
|
return plan2.dimensions.format;
|
|
10388
10471
|
}
|
|
10389
10472
|
function lockedAudio(planDir) {
|
|
10390
10473
|
const encoderJsonPath = join25(planDir, "meta", "encoder.json");
|
|
10391
10474
|
if (!existsSync18(encoderJsonPath)) return {};
|
|
10392
|
-
return JSON.parse(
|
|
10475
|
+
return JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
|
|
10393
10476
|
}
|
|
10394
10477
|
function readCfrEncoder(planDir, format) {
|
|
10395
10478
|
if (format !== "mp4") {
|
|
@@ -10401,7 +10484,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
10401
10484
|
if (!existsSync18(encoderJsonPath)) {
|
|
10402
10485
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
10403
10486
|
}
|
|
10404
|
-
const encoderJson = JSON.parse(
|
|
10487
|
+
const encoderJson = JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
|
|
10405
10488
|
if (encoderJson.encoder === "libx265-software") {
|
|
10406
10489
|
throw new Error(
|
|
10407
10490
|
`[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".`
|
|
@@ -10410,8 +10493,8 @@ function readCfrEncoder(planDir, format) {
|
|
|
10410
10493
|
return encoderJson;
|
|
10411
10494
|
}
|
|
10412
10495
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
10413
|
-
if (existsSync18(outputPath))
|
|
10414
|
-
|
|
10496
|
+
if (existsSync18(outputPath)) rmSync11(outputPath, { recursive: true, force: true });
|
|
10497
|
+
mkdirSync12(outputPath, { recursive: true });
|
|
10415
10498
|
let globalIdx = 0;
|
|
10416
10499
|
for (const chunkDir of chunkPaths) {
|
|
10417
10500
|
if (!statSync7(chunkDir).isDirectory()) {
|
|
@@ -10455,7 +10538,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
10455
10538
|
|
|
10456
10539
|
// src/services/distributed/renderChunk.ts
|
|
10457
10540
|
import { randomBytes } from "node:crypto";
|
|
10458
|
-
import { existsSync as existsSync19, mkdirSync as
|
|
10541
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13, readFileSync as readFileSync11, readdirSync as readdirSync9, rmSync as rmSync12, writeFileSync as writeFileSync10 } from "node:fs";
|
|
10459
10542
|
import { cpus } from "node:os";
|
|
10460
10543
|
import { join as join26 } from "node:path";
|
|
10461
10544
|
import {
|
|
@@ -10573,10 +10656,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
|
|
|
10573
10656
|
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
10574
10657
|
}
|
|
10575
10658
|
function hashChunkOutput(outputPath, kind) {
|
|
10576
|
-
if (kind === "file") return sha256Hex(
|
|
10659
|
+
if (kind === "file") return sha256Hex(readFileSync11(outputPath));
|
|
10577
10660
|
const entries = readdirSync9(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
10578
10661
|
const lines = entries.map(
|
|
10579
|
-
(name) => `${name}\0${sha256Hex(
|
|
10662
|
+
(name) => `${name}\0${sha256Hex(readFileSync11(join26(outputPath, name)))}`
|
|
10580
10663
|
);
|
|
10581
10664
|
return sha256Hex(lines.join("\0"));
|
|
10582
10665
|
}
|
|
@@ -10602,7 +10685,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10602
10685
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
10603
10686
|
);
|
|
10604
10687
|
}
|
|
10605
|
-
const plan2 = JSON.parse(
|
|
10688
|
+
const plan2 = JSON.parse(readFileSync11(planJsonPath, "utf-8"));
|
|
10606
10689
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
10607
10690
|
const planHashStarted = Date.now();
|
|
10608
10691
|
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
@@ -10619,13 +10702,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10619
10702
|
);
|
|
10620
10703
|
}
|
|
10621
10704
|
}
|
|
10622
|
-
const encoder = JSON.parse(
|
|
10623
|
-
const chunks = JSON.parse(
|
|
10705
|
+
const encoder = JSON.parse(readFileSync11(encoderJsonPath, "utf-8"));
|
|
10706
|
+
const chunks = JSON.parse(readFileSync11(chunksJsonPath, "utf-8"));
|
|
10624
10707
|
const videosJsonPath = join26(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
10625
10708
|
let planVideos = null;
|
|
10626
10709
|
if (existsSync19(videosJsonPath)) {
|
|
10627
10710
|
try {
|
|
10628
|
-
planVideos = validatePlanVideosForChunk(JSON.parse(
|
|
10711
|
+
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync11(videosJsonPath, "utf-8")));
|
|
10629
10712
|
} catch (err) {
|
|
10630
10713
|
if (err instanceof RenderChunkValidationError) throw err;
|
|
10631
10714
|
throw new RenderChunkValidationError(
|
|
@@ -10710,9 +10793,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10710
10793
|
forceScreenshotExplicitlyOptedOut: !encoder.forceScreenshot
|
|
10711
10794
|
};
|
|
10712
10795
|
const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
10713
|
-
|
|
10796
|
+
mkdirSync13(workDir, { recursive: true });
|
|
10714
10797
|
const framesDir = join26(workDir, "captured-frames");
|
|
10715
|
-
|
|
10798
|
+
mkdirSync13(framesDir, { recursive: true });
|
|
10716
10799
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
10717
10800
|
const chunkFrameNumbers = Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i);
|
|
10718
10801
|
const chunkFps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
|
|
@@ -10736,7 +10819,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10736
10819
|
dimensions: plan2.dimensions,
|
|
10737
10820
|
encoder,
|
|
10738
10821
|
videoCount: planVideos?.videos.length ?? 0,
|
|
10739
|
-
html:
|
|
10822
|
+
html: readFileSync11(join26(compiledDir, "index.html"), "utf-8"),
|
|
10740
10823
|
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
10741
10824
|
skipReadinessVideoIds: deferredVideos.filter(
|
|
10742
10825
|
(video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
|
|
@@ -10859,10 +10942,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10859
10942
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
10860
10943
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
10861
10944
|
if (isPngSequence) {
|
|
10862
|
-
if (!existsSync19(outputChunkPath))
|
|
10945
|
+
if (!existsSync19(outputChunkPath)) mkdirSync13(outputChunkPath, { recursive: true });
|
|
10863
10946
|
} else {
|
|
10864
10947
|
const outDir = join26(outputChunkPath, "..");
|
|
10865
|
-
if (!existsSync19(outDir))
|
|
10948
|
+
if (!existsSync19(outDir)) mkdirSync13(outDir, { recursive: true });
|
|
10866
10949
|
}
|
|
10867
10950
|
const encodeInput = {
|
|
10868
10951
|
job,
|
|
@@ -10965,8 +11048,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
10965
11048
|
return observedChunkCaptureMode(capturePerfs);
|
|
10966
11049
|
},
|
|
10967
11050
|
resetForScreenshotRetry: () => {
|
|
10968
|
-
|
|
10969
|
-
|
|
11051
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11052
|
+
mkdirSync13(framesDir, { recursive: true });
|
|
10970
11053
|
capturePerfs.length = 0;
|
|
10971
11054
|
captureAttempts.length = 0;
|
|
10972
11055
|
job.framesRendered = 0;
|
|
@@ -11008,8 +11091,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11008
11091
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
11009
11092
|
timeoutMs: cfg.ffmpegProcessTimeout
|
|
11010
11093
|
});
|
|
11011
|
-
|
|
11012
|
-
|
|
11094
|
+
rmSync12(videoOnlyPath, { force: true });
|
|
11095
|
+
rmSync12(audioScratchPath, { force: true });
|
|
11013
11096
|
}
|
|
11014
11097
|
encodeStageMs = Date.now() - encodeStarted;
|
|
11015
11098
|
} finally {
|
|
@@ -11057,8 +11140,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11057
11140
|
writeFileSync10(perfPath, `${JSON.stringify(perfPayload, null, 2)}
|
|
11058
11141
|
`, "utf-8");
|
|
11059
11142
|
try {
|
|
11060
|
-
|
|
11061
|
-
|
|
11143
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11144
|
+
rmSync12(videoFramesDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11062
11145
|
} catch (err) {
|
|
11063
11146
|
log.warn("[renderChunk] failed to remove work dir", {
|
|
11064
11147
|
workDir,
|
|
@@ -11087,17 +11170,17 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11087
11170
|
|
|
11088
11171
|
// src/services/distributed/planV2Execution.ts
|
|
11089
11172
|
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
11090
|
-
const workRoot =
|
|
11173
|
+
const workRoot = mkdtempSync6(join27(tmpdir2(), "hf-plan-v2-chunk-"));
|
|
11091
11174
|
const materializedPlanDir = join27(workRoot, "plan");
|
|
11092
11175
|
try {
|
|
11093
11176
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
11094
11177
|
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
11095
11178
|
} finally {
|
|
11096
|
-
|
|
11179
|
+
rmSync13(workRoot, { recursive: true, force: true });
|
|
11097
11180
|
}
|
|
11098
11181
|
}
|
|
11099
11182
|
async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
11100
|
-
const workRoot =
|
|
11183
|
+
const workRoot = mkdtempSync6(join27(tmpdir2(), "hf-plan-v2-assembler-"));
|
|
11101
11184
|
const materializedPlanDir = join27(workRoot, "plan");
|
|
11102
11185
|
try {
|
|
11103
11186
|
const materialized = materializePlanV2Target(
|
|
@@ -11113,7 +11196,7 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
|
11113
11196
|
options
|
|
11114
11197
|
);
|
|
11115
11198
|
} finally {
|
|
11116
|
-
|
|
11199
|
+
rmSync13(workRoot, { recursive: true, force: true });
|
|
11117
11200
|
}
|
|
11118
11201
|
}
|
|
11119
11202
|
|
|
@@ -11147,7 +11230,6 @@ var ALLOWED_RUNTIME_CAPS = ["lambda", "temporal", "cloud-run-job", "k8s-job", "n
|
|
|
11147
11230
|
var ALLOWED_HDR_MODES = ["auto", "force-sdr"];
|
|
11148
11231
|
var MAX_DIMENSION = 7680;
|
|
11149
11232
|
var MIN_DIMENSION = 16;
|
|
11150
|
-
var MAX_CHUNK_SIZE = 3600;
|
|
11151
11233
|
function validateDistributedRenderConfig(config) {
|
|
11152
11234
|
if (config === null || typeof config !== "object") {
|
|
11153
11235
|
throw new InvalidConfigError("config", "must be an object");
|
|
@@ -11363,7 +11445,7 @@ function validateIntDimension(field, value) {
|
|
|
11363
11445
|
}
|
|
11364
11446
|
|
|
11365
11447
|
// src/services/distributed/projectHash.ts
|
|
11366
|
-
import { closeSync as
|
|
11448
|
+
import { closeSync as closeSync4, openSync as openSync4, readdirSync as readdirSync10, readSync as readSync3, realpathSync as realpathSync2, statSync as statSync8 } from "node:fs";
|
|
11367
11449
|
import { createHash as createHash6 } from "node:crypto";
|
|
11368
11450
|
import { join as join28 } from "node:path";
|
|
11369
11451
|
var SITE_ID_SCHEME = "site-v2\0";
|
|
@@ -11404,14 +11486,14 @@ function hashProjectDir(projectDir, files = listSiteFiles(projectDir)) {
|
|
|
11404
11486
|
for (const rel of files) {
|
|
11405
11487
|
hash.update(rel);
|
|
11406
11488
|
hash.update("\0");
|
|
11407
|
-
const fd =
|
|
11489
|
+
const fd = openSync4(join28(projectDir, rel), "r");
|
|
11408
11490
|
try {
|
|
11409
11491
|
let read;
|
|
11410
11492
|
while ((read = readSync3(fd, chunk, 0, chunk.length, null)) > 0) {
|
|
11411
11493
|
hash.update(chunk.subarray(0, read));
|
|
11412
11494
|
}
|
|
11413
11495
|
} finally {
|
|
11414
|
-
|
|
11496
|
+
closeSync4(fd);
|
|
11415
11497
|
}
|
|
11416
11498
|
}
|
|
11417
11499
|
return hash.digest("hex").slice(0, 16);
|