@hyperframes/producer 0.9.13 → 0.9.14
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/index.js
CHANGED
|
@@ -3741,6 +3741,7 @@ function createStallClock(now = () => Date.now()) {
|
|
|
3741
3741
|
|
|
3742
3742
|
// src/services/render/stages/captureStreamingStage.ts
|
|
3743
3743
|
var DEFAULT_CAPTURE_STALL_MS = 6e4;
|
|
3744
|
+
var SESSION_INIT_STALL_MS = 3e5;
|
|
3744
3745
|
var DE_STALL_POLL_MS = 5e3;
|
|
3745
3746
|
var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
|
|
3746
3747
|
function poolFailure(error, probeSession) {
|
|
@@ -3771,6 +3772,14 @@ function captureModeLabel(mode) {
|
|
|
3771
3772
|
if (mode === "beginframe") return "BeginFrame";
|
|
3772
3773
|
return "screenshot";
|
|
3773
3774
|
}
|
|
3775
|
+
var OPENING_CAPTURE_SESSION = {
|
|
3776
|
+
captureMode: "screenshot",
|
|
3777
|
+
captureStep: "opening the capture session"
|
|
3778
|
+
};
|
|
3779
|
+
var OPENING_RISK_SESSION = {
|
|
3780
|
+
captureMode: "screenshot",
|
|
3781
|
+
captureStep: "opening the screenshot session for effect frames"
|
|
3782
|
+
};
|
|
3774
3783
|
var SequentialCaptureStallError = class extends Error {
|
|
3775
3784
|
captureMode;
|
|
3776
3785
|
constructor(session, stallTimeoutMs, frameIndex, totalFrames) {
|
|
@@ -3920,7 +3929,12 @@ async function runWorkerEncodePipelineLoop(session, totalFrames, job, currentEnc
|
|
|
3920
3929
|
const guard = createDrainFrameGuard({ log, stats, frameTime });
|
|
3921
3930
|
const guardFrame = (item, buf) => item.trusted ? Promise.resolve(buf) : guard(session, item.idx, buf);
|
|
3922
3931
|
const captureRisk = async (idx) => {
|
|
3923
|
-
await
|
|
3932
|
+
await raceAgainstStall(
|
|
3933
|
+
risk.open(),
|
|
3934
|
+
stallTimeoutMs - quiet.idle(0),
|
|
3935
|
+
{ session: OPENING_RISK_SESSION, frameIndex: idx, totalFrames, stallTimeoutMs },
|
|
3936
|
+
abortSignal
|
|
3937
|
+
);
|
|
3924
3938
|
quiet.reset();
|
|
3925
3939
|
return raceAgainstStall(
|
|
3926
3940
|
risk.capture(idx, frameTime(idx)),
|
|
@@ -4279,19 +4293,38 @@ async function runCaptureStreamingStage(input) {
|
|
|
4279
4293
|
let resumes = 0;
|
|
4280
4294
|
for (; ; ) {
|
|
4281
4295
|
const videoInjector = createRenderVideoFrameInjector();
|
|
4282
|
-
const
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4296
|
+
const initStallMs = Math.max(SESSION_INIT_STALL_MS, resolveCaptureStallTimeoutMs());
|
|
4297
|
+
const setup = createStallClock();
|
|
4298
|
+
const withinInitBudget = (work, stalled) => raceAgainstStall(
|
|
4299
|
+
work,
|
|
4300
|
+
initStallMs - setup.idle(0),
|
|
4301
|
+
{ session: stalled, frameIndex: resume.from, totalFrames, stallTimeoutMs: initStallMs },
|
|
4302
|
+
abortSignal
|
|
4288
4303
|
);
|
|
4304
|
+
const openSession = async () => {
|
|
4305
|
+
const opening = createCaptureSession(
|
|
4306
|
+
fileServer.url,
|
|
4307
|
+
framesDir,
|
|
4308
|
+
buildCaptureOptions(),
|
|
4309
|
+
videoInjector,
|
|
4310
|
+
captureCfg
|
|
4311
|
+
);
|
|
4312
|
+
try {
|
|
4313
|
+
return await withinInitBudget(opening, OPENING_CAPTURE_SESSION);
|
|
4314
|
+
} catch (error) {
|
|
4315
|
+
void opening.then(closeCaptureSession, () => {
|
|
4316
|
+
});
|
|
4317
|
+
throw error;
|
|
4318
|
+
}
|
|
4319
|
+
};
|
|
4320
|
+
const session = probeSession ?? await openSession();
|
|
4289
4321
|
captureBeyondViewport = session.options.captureBeyondViewport;
|
|
4290
4322
|
if (probeSession) {
|
|
4291
4323
|
prepareCaptureSessionForReuse(session, framesDir, videoInjector);
|
|
4292
4324
|
probeSession = null;
|
|
4293
4325
|
}
|
|
4294
4326
|
let riskSession = null;
|
|
4327
|
+
let attemptClosed = false;
|
|
4295
4328
|
const openRiskSession = async () => {
|
|
4296
4329
|
const opened = await createCaptureSession(
|
|
4297
4330
|
fileServer.url,
|
|
@@ -4305,6 +4338,10 @@ async function runCaptureStreamingStage(input) {
|
|
|
4305
4338
|
enableBrowserPool: false
|
|
4306
4339
|
}
|
|
4307
4340
|
);
|
|
4341
|
+
if (attemptClosed) {
|
|
4342
|
+
await closeCaptureSession(opened);
|
|
4343
|
+
throw new Error("[Render] Capture attempt ended while its screenshot session opened");
|
|
4344
|
+
}
|
|
4308
4345
|
riskSession = opened;
|
|
4309
4346
|
await initializeSession(opened);
|
|
4310
4347
|
return opened;
|
|
@@ -4312,9 +4349,9 @@ async function runCaptureStreamingStage(input) {
|
|
|
4312
4349
|
let resumed = false;
|
|
4313
4350
|
try {
|
|
4314
4351
|
if (!session.isInitialized) {
|
|
4315
|
-
await initializeSession(session);
|
|
4352
|
+
await withinInitBudget(initializeSession(session), session);
|
|
4316
4353
|
}
|
|
4317
|
-
await completeDeferredDrawElementInit(session);
|
|
4354
|
+
await withinInitBudget(completeDeferredDrawElementInit(session), session);
|
|
4318
4355
|
assertNotAborted();
|
|
4319
4356
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
4320
4357
|
if (session.workerEncodeEnabled) {
|
|
@@ -4397,6 +4434,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4397
4434
|
resumes++;
|
|
4398
4435
|
resumed = true;
|
|
4399
4436
|
} finally {
|
|
4437
|
+
attemptClosed = true;
|
|
4400
4438
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
4401
4439
|
await closeCaptureSession(session);
|
|
4402
4440
|
if (riskSession) await closeCaptureSession(riskSession);
|
|
@@ -5863,7 +5901,7 @@ import { suggestMatchingPreset } from "@hyperframes/core";
|
|
|
5863
5901
|
// src/services/htmlCompiler.ts
|
|
5864
5902
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
5865
5903
|
import { createHash as createHash6 } from "node:crypto";
|
|
5866
|
-
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7 } from "fs";
|
|
5904
|
+
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7 } from "fs";
|
|
5867
5905
|
import { join as join15, dirname as dirname7, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
|
|
5868
5906
|
import { parseHTML as parseHTML5 } from "linkedom";
|
|
5869
5907
|
import {
|
|
@@ -7838,6 +7876,25 @@ ${fontFaceBlocks.join("\n")}
|
|
|
7838
7876
|
}
|
|
7839
7877
|
return result;
|
|
7840
7878
|
}
|
|
7879
|
+
function projectStylesheetFontFamilies(html, projectDir) {
|
|
7880
|
+
const families = [];
|
|
7881
|
+
for (const [tag] of html.matchAll(/<link\b[^>]*\brel=["']stylesheet["'][^>]*>/gi)) {
|
|
7882
|
+
const href = /\bhref=["']([^"']+)["']/i.exec(tag)?.[1];
|
|
7883
|
+
if (!href || /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href)) continue;
|
|
7884
|
+
const sheet = resolve7(projectDir, decodedUrlPath(href).replace(/^(?:\.?\/)+/, ""));
|
|
7885
|
+
if (!isPathInside2(sheet, projectDir) || !existsSync10(sheet)) continue;
|
|
7886
|
+
for (const face of extractFontFaceBlocks(readFileSync7(sheet, "utf8"))) {
|
|
7887
|
+
const family = /font-family\s*:\s*([^;}]+)/i.exec(face)?.[1]?.trim().replace(/^['"]|['"]$/g, "");
|
|
7888
|
+
const shipsFile = [...face.matchAll(LOCAL_FONTFACE_URL_RE)].some(([, url = ""]) => {
|
|
7889
|
+
const path = decodedUrlPath(url);
|
|
7890
|
+
const file = path.startsWith("/") ? resolve7(projectDir, `.${path}`) : resolve7(dirname7(sheet), path);
|
|
7891
|
+
return isPathInside2(file, projectDir) && existsSync10(file);
|
|
7892
|
+
});
|
|
7893
|
+
if (family && shipsFile) families.push(family);
|
|
7894
|
+
}
|
|
7895
|
+
}
|
|
7896
|
+
return families;
|
|
7897
|
+
}
|
|
7841
7898
|
function collectFontFaceUrls(html) {
|
|
7842
7899
|
const styleBlockRe = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
7843
7900
|
const urlSet = /* @__PURE__ */ new Set();
|
|
@@ -7869,11 +7926,12 @@ function collectFontFaceUrls(html) {
|
|
|
7869
7926
|
return urlSet;
|
|
7870
7927
|
}
|
|
7871
7928
|
async function localizeRemoteFontFaces(html, downloadDir) {
|
|
7872
|
-
|
|
7873
|
-
|
|
7929
|
+
return localizeFontFaceUrls(await inlineExternalFontStylesheets(html), downloadDir);
|
|
7930
|
+
}
|
|
7931
|
+
function localizeFontFaceUrls(html, downloadDir) {
|
|
7874
7932
|
return downloadAndRewriteUrls(
|
|
7875
|
-
|
|
7876
|
-
|
|
7933
|
+
collectFontFaceUrls(html),
|
|
7934
|
+
html,
|
|
7877
7935
|
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7878
7936
|
"Remote font download failed",
|
|
7879
7937
|
"Localized remote font face(s)",
|
|
@@ -8041,15 +8099,17 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
8041
8099
|
const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
|
|
8042
8100
|
const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
|
|
8043
8101
|
const hasAncestorBackgroundImage = detectAncestorBackgroundImage(sanitizedHtml);
|
|
8102
|
+
const fontSheetHtml = await inlineExternalFontStylesheets(
|
|
8103
|
+
promoteCssImportsToLinkTags(sanitizedHtml)
|
|
8104
|
+
);
|
|
8044
8105
|
const normalizedFontHtml = normalizeSystemFontPrimaryFamilies(
|
|
8045
|
-
injectTextRenderingRule(
|
|
8046
|
-
coalesceHeadStylesAndBodyScripts(promoteCssImportsToLinkTags(sanitizedHtml))
|
|
8047
|
-
)
|
|
8106
|
+
injectTextRenderingRule(coalesceHeadStylesAndBodyScripts(fontSheetHtml))
|
|
8048
8107
|
);
|
|
8049
8108
|
const coalescedHtml = await injectDeterministicFontFaces(normalizedFontHtml, {
|
|
8050
8109
|
logger: defaultLogger,
|
|
8051
8110
|
failClosedFontFetch: options.failClosedFontFetch === true,
|
|
8052
8111
|
allowSystemFontCapture: options.allowSystemFontCapture,
|
|
8112
|
+
projectFontFamilies: projectStylesheetFontFamilies(fontSheetHtml, projectDir),
|
|
8053
8113
|
abortSignal: options.abortSignal
|
|
8054
8114
|
});
|
|
8055
8115
|
const assembledHtml = await inlineExternalScripts(coalescedHtml);
|
|
@@ -8075,7 +8135,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
8075
8135
|
);
|
|
8076
8136
|
const { html: htmlWithLocalImages, remoteMediaAssets: remoteImageAssets } = await localizeRemoteImageSources(htmlWithLocalMedia, downloadDir);
|
|
8077
8137
|
const { html: htmlWithLocalBg, remoteMediaAssets: remoteBgAssets } = await localizeRemoteBackgroundImages(htmlWithLocalImages, downloadDir);
|
|
8078
|
-
const { html: htmlWithLocalizedFonts, remoteMediaAssets: remoteFontAssets } = await
|
|
8138
|
+
const { html: htmlWithLocalizedFonts, remoteMediaAssets: remoteFontAssets } = await localizeFontFaceUrls(htmlWithLocalBg, downloadDir);
|
|
8079
8139
|
const {
|
|
8080
8140
|
html: htmlWithJpegs,
|
|
8081
8141
|
preparedAssets: preparedHeifAssets,
|
|
@@ -11950,7 +12010,7 @@ import {
|
|
|
11950
12010
|
existsSync as existsSync17,
|
|
11951
12011
|
mkdirSync as mkdirSync9,
|
|
11952
12012
|
readdirSync as readdirSync4,
|
|
11953
|
-
readFileSync as
|
|
12013
|
+
readFileSync as readFileSync8,
|
|
11954
12014
|
rmSync as rmSync9,
|
|
11955
12015
|
statSync as statSync7,
|
|
11956
12016
|
writeFileSync as writeFileSync5
|
|
@@ -12044,7 +12104,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
12044
12104
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
12045
12105
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
12046
12106
|
}
|
|
12047
|
-
const gif =
|
|
12107
|
+
const gif = readFileSync8(outputPath);
|
|
12048
12108
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
12049
12109
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
12050
12110
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
@@ -12077,7 +12137,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
12077
12137
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
12078
12138
|
}
|
|
12079
12139
|
}
|
|
12080
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
12140
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync8(outputPath)) !== false) {
|
|
12081
12141
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
12082
12142
|
if (failure) return failure;
|
|
12083
12143
|
}
|
|
@@ -15481,7 +15541,7 @@ import {
|
|
|
15481
15541
|
REFERENCE_BACKGROUND_FIELD as REFERENCE_BACKGROUND_FIELD4,
|
|
15482
15542
|
referenceBackgroundResult as referenceBackgroundResult6
|
|
15483
15543
|
} from "@hyperframes/engine";
|
|
15484
|
-
import { mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as
|
|
15544
|
+
import { mkdirSync as mkdirSync18, mkdtempSync as mkdtempSync7, readFileSync as readFileSync14, rmSync as rmSync16 } from "node:fs";
|
|
15485
15545
|
import { join as join42 } from "node:path";
|
|
15486
15546
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
15487
15547
|
|
|
@@ -15921,7 +15981,7 @@ import {
|
|
|
15921
15981
|
// src/services/distributed/shared.ts
|
|
15922
15982
|
import { execFile as execFileCallback } from "node:child_process";
|
|
15923
15983
|
import { dirname as dirname14, join as join30 } from "node:path";
|
|
15924
|
-
import { existsSync as existsSync19, readFileSync as
|
|
15984
|
+
import { existsSync as existsSync19, readFileSync as readFileSync9 } from "node:fs";
|
|
15925
15985
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
15926
15986
|
import { promisify as promisify2 } from "node:util";
|
|
15927
15987
|
import {
|
|
@@ -16200,7 +16260,7 @@ function readProducerVersion() {
|
|
|
16200
16260
|
const candidate = join30(current, "package.json");
|
|
16201
16261
|
if (existsSync19(candidate)) {
|
|
16202
16262
|
try {
|
|
16203
|
-
const pkg = JSON.parse(
|
|
16263
|
+
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
16204
16264
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
16205
16265
|
cachedProducerVersion = pkg.version;
|
|
16206
16266
|
return pkg.version;
|
|
@@ -16586,7 +16646,7 @@ async function readPlanReferenceBackground(input) {
|
|
|
16586
16646
|
}
|
|
16587
16647
|
|
|
16588
16648
|
// src/services/render/stages/freezePlan.ts
|
|
16589
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as
|
|
16649
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync12, readFileSync as readFileSync10, readdirSync as readdirSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
16590
16650
|
import { join as join34, relative as relative3, resolve as resolve9 } from "node:path";
|
|
16591
16651
|
import {
|
|
16592
16652
|
REFERENCE_BACKGROUND_FIELD,
|
|
@@ -16826,7 +16886,7 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
16826
16886
|
const assets = [];
|
|
16827
16887
|
for (const file of files) {
|
|
16828
16888
|
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
16829
|
-
compositionHtml =
|
|
16889
|
+
compositionHtml = readFileSync10(file.absolutePath);
|
|
16830
16890
|
continue;
|
|
16831
16891
|
}
|
|
16832
16892
|
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
@@ -16850,7 +16910,7 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
16850
16910
|
}
|
|
16851
16911
|
function readPlanRemoteFiles(planDir) {
|
|
16852
16912
|
const path = join34(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
16853
|
-
return existsSync21(path) ? JSON.parse(
|
|
16913
|
+
return existsSync21(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
16854
16914
|
}
|
|
16855
16915
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
16856
16916
|
const planJsonPath = join34(planDir, "plan.json");
|
|
@@ -16861,8 +16921,8 @@ function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
|
16861
16921
|
if (!existsSync21(encoderJsonPath)) {
|
|
16862
16922
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
16863
16923
|
}
|
|
16864
|
-
const planJson = JSON.parse(
|
|
16865
|
-
const encoderConfigCanonicalJson =
|
|
16924
|
+
const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
16925
|
+
const encoderConfigCanonicalJson = readFileSync10(encoderJsonPath, "utf-8");
|
|
16866
16926
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
16867
16927
|
return computePlanHash({
|
|
16868
16928
|
compositionHtml,
|
|
@@ -16969,7 +17029,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
16969
17029
|
}
|
|
16970
17030
|
|
|
16971
17031
|
// src/services/distributed/chunkAudio.ts
|
|
16972
|
-
import { closeSync as closeSync6, existsSync as existsSync22, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as
|
|
17032
|
+
import { closeSync as closeSync6, existsSync as existsSync22, ftruncateSync, mkdirSync as mkdirSync13, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
|
|
16973
17033
|
import { writeFileSync as writeFileSync8, writeSync } from "node:fs";
|
|
16974
17034
|
import { join as join35 } from "node:path";
|
|
16975
17035
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
@@ -17057,7 +17117,7 @@ async function writeChunkAudioSlices(input) {
|
|
|
17057
17117
|
offset += track.sizes[offsets.length - 1];
|
|
17058
17118
|
}
|
|
17059
17119
|
});
|
|
17060
|
-
const bytes =
|
|
17120
|
+
const bytes = readFileSync11(input.audioPath);
|
|
17061
17121
|
const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
|
|
17062
17122
|
mkdirSync13(join35(input.planDir, SLICE_DIR), { recursive: true });
|
|
17063
17123
|
input.chunks.forEach((_, index) => {
|
|
@@ -17076,8 +17136,8 @@ async function writeChunkAudioSlices(input) {
|
|
|
17076
17136
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
17077
17137
|
const layoutPath = join35(planDir, SLICE_LAYOUT);
|
|
17078
17138
|
if (!existsSync22(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
17079
|
-
const layout = JSON.parse(
|
|
17080
|
-
const frame =
|
|
17139
|
+
const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
|
|
17140
|
+
const frame = readFileSync11(join35(planDir, SLICE_FRAME));
|
|
17081
17141
|
const fd = openSync6(scratchPath, "w");
|
|
17082
17142
|
try {
|
|
17083
17143
|
writeSync(fd, frame, 0, layout.payloadStart, 0);
|
|
@@ -17090,7 +17150,7 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
|
17090
17150
|
);
|
|
17091
17151
|
const start = layout.partStarts[chunkIndex];
|
|
17092
17152
|
if (start !== void 0) {
|
|
17093
|
-
const part =
|
|
17153
|
+
const part = readFileSync11(join35(planDir, chunkAudioSlicePath(chunkIndex)));
|
|
17094
17154
|
writeSync(fd, part, 0, part.length, start);
|
|
17095
17155
|
}
|
|
17096
17156
|
ftruncateSync(fd, layout.sizeBytes);
|
|
@@ -18171,7 +18231,7 @@ async function plan(projectDir, config, planDir, options = {}) {
|
|
|
18171
18231
|
|
|
18172
18232
|
// src/services/distributed/renderChunk.ts
|
|
18173
18233
|
import { randomBytes } from "node:crypto";
|
|
18174
|
-
import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as
|
|
18234
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync17, readFileSync as readFileSync13, readdirSync as readdirSync10, rmSync as rmSync15, writeFileSync as writeFileSync12 } from "node:fs";
|
|
18175
18235
|
import { cpus as cpus3 } from "node:os";
|
|
18176
18236
|
import { join as join41 } from "node:path";
|
|
18177
18237
|
import {
|
|
@@ -18202,7 +18262,7 @@ import {
|
|
|
18202
18262
|
lstatSync as lstatSync3,
|
|
18203
18263
|
mkdirSync as mkdirSync16,
|
|
18204
18264
|
mkdtempSync as mkdtempSync6,
|
|
18205
|
-
readFileSync as
|
|
18265
|
+
readFileSync as readFileSync12,
|
|
18206
18266
|
readdirSync as readdirSync9,
|
|
18207
18267
|
rmSync as rmSync14,
|
|
18208
18268
|
statSync as statSync10,
|
|
@@ -18351,7 +18411,7 @@ function isNonNegativeIntegerArray(value) {
|
|
|
18351
18411
|
return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
|
|
18352
18412
|
}
|
|
18353
18413
|
function readJsonFile(path, label) {
|
|
18354
|
-
const contents =
|
|
18414
|
+
const contents = readFileSync12(path, "utf-8");
|
|
18355
18415
|
try {
|
|
18356
18416
|
const parsed = JSON.parse(contents);
|
|
18357
18417
|
return parsed;
|
|
@@ -19185,10 +19245,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
|
|
|
19185
19245
|
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
19186
19246
|
}
|
|
19187
19247
|
function hashChunkOutput(outputPath, kind) {
|
|
19188
|
-
if (kind === "file") return sha256Hex(
|
|
19248
|
+
if (kind === "file") return sha256Hex(readFileSync13(outputPath));
|
|
19189
19249
|
const entries = readdirSync10(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
19190
19250
|
const lines = entries.map(
|
|
19191
|
-
(name) => `${name}\0${sha256Hex(
|
|
19251
|
+
(name) => `${name}\0${sha256Hex(readFileSync13(join41(outputPath, name)))}`
|
|
19192
19252
|
);
|
|
19193
19253
|
return sha256Hex(lines.join("\0"));
|
|
19194
19254
|
}
|
|
@@ -19214,7 +19274,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19214
19274
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
19215
19275
|
);
|
|
19216
19276
|
}
|
|
19217
|
-
const plan2 = JSON.parse(
|
|
19277
|
+
const plan2 = JSON.parse(readFileSync13(planJsonPath, "utf-8"));
|
|
19218
19278
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
19219
19279
|
const planHashStarted = Date.now();
|
|
19220
19280
|
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
@@ -19231,13 +19291,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19231
19291
|
);
|
|
19232
19292
|
}
|
|
19233
19293
|
}
|
|
19234
|
-
const encoder = JSON.parse(
|
|
19235
|
-
const chunks = JSON.parse(
|
|
19294
|
+
const encoder = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
|
|
19295
|
+
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
19236
19296
|
const videosJsonPath = join41(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19237
19297
|
let planVideos = null;
|
|
19238
19298
|
if (existsSync26(videosJsonPath)) {
|
|
19239
19299
|
try {
|
|
19240
|
-
planVideos = validatePlanVideosForChunk(JSON.parse(
|
|
19300
|
+
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
|
|
19241
19301
|
} catch (err) {
|
|
19242
19302
|
if (err instanceof RenderChunkValidationError) throw err;
|
|
19243
19303
|
throw new RenderChunkValidationError(
|
|
@@ -19348,7 +19408,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19348
19408
|
dimensions: plan2.dimensions,
|
|
19349
19409
|
encoder,
|
|
19350
19410
|
videoCount: planVideos?.videos.length ?? 0,
|
|
19351
|
-
html:
|
|
19411
|
+
html: readFileSync13(join41(compiledDir, "index.html"), "utf-8"),
|
|
19352
19412
|
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
19353
19413
|
skipReadinessVideoIds: deferredVideos.filter(
|
|
19354
19414
|
(video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
|
|
@@ -19810,7 +19870,7 @@ async function renderSpan(projectDir, config, span, outputPath) {
|
|
|
19810
19870
|
planned.totalFrames
|
|
19811
19871
|
);
|
|
19812
19872
|
const slices = JSON.parse(
|
|
19813
|
-
|
|
19873
|
+
readFileSync14(join42(planDir, "meta", "chunks.json"), "utf-8")
|
|
19814
19874
|
);
|
|
19815
19875
|
const parts = [];
|
|
19816
19876
|
for (const slice of slices) {
|
|
@@ -20443,7 +20503,7 @@ import {
|
|
|
20443
20503
|
// src/services/hyperframeLint.ts
|
|
20444
20504
|
import {
|
|
20445
20505
|
existsSync as existsSync27,
|
|
20446
|
-
readFileSync as
|
|
20506
|
+
readFileSync as readFileSync15,
|
|
20447
20507
|
statSync as statSync11,
|
|
20448
20508
|
openSync as openSync7,
|
|
20449
20509
|
fstatSync as fstatSync3,
|
|
@@ -20489,7 +20549,7 @@ function readEntryHtml(filePath) {
|
|
|
20489
20549
|
}
|
|
20490
20550
|
try {
|
|
20491
20551
|
if (!fstatSync3(fd).isFile()) return null;
|
|
20492
|
-
return
|
|
20552
|
+
return readFileSync15(fd, "utf-8");
|
|
20493
20553
|
} finally {
|
|
20494
20554
|
closeSync7(fd);
|
|
20495
20555
|
}
|
|
@@ -21338,7 +21398,7 @@ import {
|
|
|
21338
21398
|
existsSync as existsSync30,
|
|
21339
21399
|
mkdirSync as mkdirSync20,
|
|
21340
21400
|
mkdtempSync as mkdtempSync9,
|
|
21341
|
-
readFileSync as
|
|
21401
|
+
readFileSync as readFileSync16,
|
|
21342
21402
|
readdirSync as readdirSync11,
|
|
21343
21403
|
rmSync as rmSync18,
|
|
21344
21404
|
statSync as statSync13,
|
|
@@ -21447,13 +21507,13 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
21447
21507
|
if (!existsSync30(planJsonPath)) {
|
|
21448
21508
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
21449
21509
|
}
|
|
21450
|
-
const plan2 = JSON.parse(
|
|
21510
|
+
const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
|
|
21451
21511
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
21452
21512
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
21453
21513
|
if (!existsSync30(chunksJsonPath)) {
|
|
21454
21514
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
21455
21515
|
}
|
|
21456
|
-
const chunks = JSON.parse(
|
|
21516
|
+
const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
|
|
21457
21517
|
if (chunkPaths.length !== chunks.length) {
|
|
21458
21518
|
throw new Error(
|
|
21459
21519
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
@@ -21599,13 +21659,13 @@ function stitchesSeparateAudio(planDir) {
|
|
|
21599
21659
|
return lockedAudio(planDir).separateAudio === true;
|
|
21600
21660
|
}
|
|
21601
21661
|
function planFormat(planDir) {
|
|
21602
|
-
const plan2 = JSON.parse(
|
|
21662
|
+
const plan2 = JSON.parse(readFileSync16(join46(planDir, "plan.json"), "utf-8"));
|
|
21603
21663
|
return plan2.dimensions.format;
|
|
21604
21664
|
}
|
|
21605
21665
|
function lockedAudio(planDir) {
|
|
21606
21666
|
const encoderJsonPath = join46(planDir, "meta", "encoder.json");
|
|
21607
21667
|
if (!existsSync30(encoderJsonPath)) return {};
|
|
21608
|
-
return JSON.parse(
|
|
21668
|
+
return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
21609
21669
|
}
|
|
21610
21670
|
function readCfrEncoder(planDir, format) {
|
|
21611
21671
|
if (format !== "mp4") {
|
|
@@ -21617,7 +21677,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
21617
21677
|
if (!existsSync30(encoderJsonPath)) {
|
|
21618
21678
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
21619
21679
|
}
|
|
21620
|
-
const encoderJson = JSON.parse(
|
|
21680
|
+
const encoderJson = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
21621
21681
|
if (encoderJson.encoder === "libx265-software") {
|
|
21622
21682
|
throw new Error(
|
|
21623
21683
|
`[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".`
|