@hyperframes/producer 0.9.18 → 0.9.20
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 +235 -125
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +73 -73
- package/dist/index.js +259 -170
- package/dist/public-server.js +178 -100
- package/dist/services/render/stages/captureStreamingStage.d.ts +11 -2
- package/dist/services/renderOrchestrator.d.ts +3 -2
- package/dist/utils/paths.d.ts +2 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
// src/services/renderOrchestrator.ts
|
|
10
10
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
11
11
|
import {
|
|
12
|
-
existsSync as
|
|
12
|
+
existsSync as existsSync21,
|
|
13
13
|
mkdirSync as mkdirSync12,
|
|
14
14
|
readdirSync as readdirSync6,
|
|
15
15
|
rmSync as rmSync12,
|
|
@@ -918,7 +918,7 @@ function resolveDeliveryEncoderOptions(config) {
|
|
|
918
918
|
import {
|
|
919
919
|
copyFileSync,
|
|
920
920
|
cpSync,
|
|
921
|
-
existsSync as
|
|
921
|
+
existsSync as existsSync4,
|
|
922
922
|
mkdirSync,
|
|
923
923
|
rmSync,
|
|
924
924
|
symlinkSync,
|
|
@@ -938,7 +938,9 @@ import {
|
|
|
938
938
|
relative as nodeRelative,
|
|
939
939
|
isAbsolute as nodeIsAbsolute
|
|
940
940
|
} from "node:path";
|
|
941
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
941
942
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
943
|
+
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
942
944
|
var DEFAULT_RENDERS_DIR = process.env.PRODUCER_RENDERS_DIR ?? // fileURLToPath (not URL.pathname): on Windows .pathname is "/D:/..." which
|
|
943
945
|
// resolves to a bogus renders dir.
|
|
944
946
|
nodeResolve(fileURLToPath2(import.meta.url), "../../..", "renders");
|
|
@@ -976,6 +978,13 @@ function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS
|
|
|
976
978
|
const absoluteOutputPath = nodeResolve(resolvedOutputPath);
|
|
977
979
|
return { absoluteProjectDir, absoluteOutputPath };
|
|
978
980
|
}
|
|
981
|
+
function resolveCompositionSrc(projectDir, src) {
|
|
982
|
+
const rooted = (value) => nodeResolve(projectDir, value.replace(/^(?:\.?[\\/])+/, ""));
|
|
983
|
+
const decoded = rooted(decodedUrlPath(src));
|
|
984
|
+
if (!isPathInside2(decoded, projectDir)) return null;
|
|
985
|
+
const raw = rooted(src);
|
|
986
|
+
return !existsSync3(decoded) && isPathInside2(raw, projectDir) && existsSync3(raw) ? raw : decoded;
|
|
987
|
+
}
|
|
979
988
|
|
|
980
989
|
// src/services/render/shared.ts
|
|
981
990
|
var BROWSER_MEDIA_EPSILON = 1e-4;
|
|
@@ -1179,7 +1188,7 @@ var materializePathModule = {
|
|
|
1179
1188
|
isAbsolute
|
|
1180
1189
|
};
|
|
1181
1190
|
var materializeFileSystem = {
|
|
1182
|
-
existsSync:
|
|
1191
|
+
existsSync: existsSync4,
|
|
1183
1192
|
mkdirSync,
|
|
1184
1193
|
symlinkSync,
|
|
1185
1194
|
cpSync,
|
|
@@ -1481,7 +1490,7 @@ var RenderExecutionContext = class {
|
|
|
1481
1490
|
};
|
|
1482
1491
|
|
|
1483
1492
|
// src/services/render/artifactTransaction.ts
|
|
1484
|
-
import { closeSync as closeSync2, existsSync as
|
|
1493
|
+
import { closeSync as closeSync2, existsSync as existsSync6, fstatSync as fstatSync2, openSync as openSync2, readSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
|
|
1485
1494
|
import { basename as basename3, dirname as dirname4, extname as extname2, join as join5, resolve as resolve4 } from "node:path";
|
|
1486
1495
|
import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, renameWithRetry } from "@hyperframes/engine";
|
|
1487
1496
|
|
|
@@ -1494,7 +1503,7 @@ import {
|
|
|
1494
1503
|
|
|
1495
1504
|
// src/services/render/renderDirOwner.ts
|
|
1496
1505
|
import {
|
|
1497
|
-
existsSync as
|
|
1506
|
+
existsSync as existsSync5,
|
|
1498
1507
|
lstatSync,
|
|
1499
1508
|
mkdtempSync,
|
|
1500
1509
|
readFileSync as readFileSync2,
|
|
@@ -1570,7 +1579,7 @@ function newestWriteMs(path) {
|
|
|
1570
1579
|
}
|
|
1571
1580
|
function isAbandoned(dir, ownerlessIdleMs, now) {
|
|
1572
1581
|
try {
|
|
1573
|
-
if (!lstatSync(dir).isDirectory() ||
|
|
1582
|
+
if (!lstatSync(dir).isDirectory() || existsSync5(join4(dir, TRANSACTION_BACKUP))) return false;
|
|
1574
1583
|
const owner = ownerState(dir);
|
|
1575
1584
|
if (owner === "gone") return true;
|
|
1576
1585
|
return owner === "none" && ownerlessIdleMs !== void 0 && now - newestWriteMs(dir) >= ownerlessIdleMs;
|
|
@@ -1602,7 +1611,7 @@ function createRenderWorkDir(prefix, outputDir) {
|
|
|
1602
1611
|
|
|
1603
1612
|
// src/services/render/artifactTransaction.ts
|
|
1604
1613
|
var defaultFileSystem = {
|
|
1605
|
-
existsSync:
|
|
1614
|
+
existsSync: existsSync6,
|
|
1606
1615
|
renameSync: (source, destination) => renameWithRetry(source, destination),
|
|
1607
1616
|
rmSync: rmSync3
|
|
1608
1617
|
};
|
|
@@ -1756,7 +1765,7 @@ var ArtifactTransaction = class {
|
|
|
1756
1765
|
async assertDirectoryExpectation(expected, files) {
|
|
1757
1766
|
for (const required of expected.requiredFiles ?? []) {
|
|
1758
1767
|
const path = join5(this.stagingPath, required);
|
|
1759
|
-
if (!
|
|
1768
|
+
if (!existsSync6(path)) {
|
|
1760
1769
|
throw new Error(`Render artifact directory is missing ${required}: ${this.stagingPath}`);
|
|
1761
1770
|
}
|
|
1762
1771
|
assertReadableNonEmptyFile(path);
|
|
@@ -2005,7 +2014,7 @@ function replanAfterFailure(plan2, failure) {
|
|
|
2005
2014
|
}
|
|
2006
2015
|
|
|
2007
2016
|
// src/services/render/stages/captureSegmentedStage.ts
|
|
2008
|
-
import { existsSync as
|
|
2017
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, rmSync as rmSync6, statSync as statSync3 } from "node:fs";
|
|
2009
2018
|
import { join as join10 } from "node:path";
|
|
2010
2019
|
import {
|
|
2011
2020
|
captureFrameToBuffer as captureFrameToBuffer2,
|
|
@@ -2302,6 +2311,19 @@ function aggregateBeginFrameReuse(perfs) {
|
|
|
2302
2311
|
if (noDamageFrames + hasDamageFrames === 0) return void 0;
|
|
2303
2312
|
return { noDamageFrames, hasDamageFrames };
|
|
2304
2313
|
}
|
|
2314
|
+
function aggregateMotionBlur(perfs) {
|
|
2315
|
+
const ran = perfs.flatMap((p) => p.motionBlur ?? []).filter((m) => m.tier || m.failedFrames > 0);
|
|
2316
|
+
const first = ran[0];
|
|
2317
|
+
if (!first) return void 0;
|
|
2318
|
+
return {
|
|
2319
|
+
tier: first.tier ?? ran.find((m) => m.tier)?.tier ?? null,
|
|
2320
|
+
reasonCode: first.reasonCode,
|
|
2321
|
+
movers: first.movers,
|
|
2322
|
+
failedFrames: ran.reduce((sum2, m) => sum2 + m.failedFrames, 0),
|
|
2323
|
+
readEvents: ran.some((m) => m.readEvents),
|
|
2324
|
+
fingerprint: ran.find((m) => m.fingerprint)?.fingerprint ?? null
|
|
2325
|
+
};
|
|
2326
|
+
}
|
|
2305
2327
|
function buildRenderPerfSummary(input) {
|
|
2306
2328
|
return {
|
|
2307
2329
|
renderId: input.job.id,
|
|
@@ -2352,6 +2374,7 @@ function buildRenderPerfSummary(input) {
|
|
|
2352
2374
|
staticDedup: aggregateDedup(input.dedupPerfs),
|
|
2353
2375
|
chromeMemory: aggregateChromeMemory(input.dedupPerfs),
|
|
2354
2376
|
beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
|
|
2377
|
+
motionBlur: aggregateMotionBlur(input.dedupPerfs),
|
|
2355
2378
|
drawElement: aggregateDrawElement(
|
|
2356
2379
|
input.dedupPerfs,
|
|
2357
2380
|
input.drawElement ?? { selfVerifyFallback: false }
|
|
@@ -3826,6 +3849,21 @@ function raceAgainstStall(promise, deadlineMs, input, signal) {
|
|
|
3826
3849
|
);
|
|
3827
3850
|
});
|
|
3828
3851
|
}
|
|
3852
|
+
function createSessionInitBudget(input) {
|
|
3853
|
+
const initStallMs = Math.max(SESSION_INIT_STALL_MS, resolveCaptureStallTimeoutMs());
|
|
3854
|
+
const setup = createStallClock();
|
|
3855
|
+
return (work, stalled) => raceAgainstStall(
|
|
3856
|
+
work,
|
|
3857
|
+
initStallMs - setup.idle(0),
|
|
3858
|
+
{
|
|
3859
|
+
session: stalled,
|
|
3860
|
+
frameIndex: input.frameIndex,
|
|
3861
|
+
totalFrames: input.totalFrames,
|
|
3862
|
+
stallTimeoutMs: initStallMs
|
|
3863
|
+
},
|
|
3864
|
+
input.abortSignal
|
|
3865
|
+
);
|
|
3866
|
+
}
|
|
3829
3867
|
function createDrainFrameGuard(args) {
|
|
3830
3868
|
const { log, stats, frameTime } = args;
|
|
3831
3869
|
const verifyMinDb = resolveDeVerifyMinDb();
|
|
@@ -4298,14 +4336,11 @@ async function runCaptureStreamingStage(input) {
|
|
|
4298
4336
|
let resumes = 0;
|
|
4299
4337
|
for (; ; ) {
|
|
4300
4338
|
const videoInjector = createRenderVideoFrameInjector();
|
|
4301
|
-
const
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
work,
|
|
4305
|
-
initStallMs - setup.idle(0),
|
|
4306
|
-
{ session: stalled, frameIndex: resume.from, totalFrames, stallTimeoutMs: initStallMs },
|
|
4339
|
+
const withinInitBudget = createSessionInitBudget({
|
|
4340
|
+
frameIndex: resume.from,
|
|
4341
|
+
totalFrames,
|
|
4307
4342
|
abortSignal
|
|
4308
|
-
);
|
|
4343
|
+
});
|
|
4309
4344
|
const openSession = async () => {
|
|
4310
4345
|
const opening = createCaptureSession(
|
|
4311
4346
|
fileServer.url,
|
|
@@ -4583,7 +4618,7 @@ async function captureSegmentToEncoder(ctx, segment) {
|
|
|
4583
4618
|
path: segmentPath,
|
|
4584
4619
|
// Recorded so resume can prove the file on disk is the one that
|
|
4585
4620
|
// finished; absent only in unit tests, whose fake encoder writes none.
|
|
4586
|
-
bytes:
|
|
4621
|
+
bytes: existsSync7(segmentPath) ? statSync3(segmentPath).size : 0
|
|
4587
4622
|
});
|
|
4588
4623
|
return encodeResult;
|
|
4589
4624
|
} finally {
|
|
@@ -4613,14 +4648,29 @@ function defaultSessionFactory(input) {
|
|
|
4613
4648
|
});
|
|
4614
4649
|
probeSession = null;
|
|
4615
4650
|
}
|
|
4616
|
-
const
|
|
4651
|
+
const withinInitBudget = createSessionInitBudget({
|
|
4652
|
+
frameIndex: input.job.framesRendered ?? 0,
|
|
4653
|
+
totalFrames: input.totalFrames,
|
|
4654
|
+
abortSignal: input.abortSignal
|
|
4655
|
+
});
|
|
4656
|
+
const openSession = async () => {
|
|
4657
|
+
const opening = openSegmentedSession(input, injector);
|
|
4658
|
+
try {
|
|
4659
|
+
return await withinInitBudget(opening, OPENING_CAPTURE_SESSION);
|
|
4660
|
+
} catch (error) {
|
|
4661
|
+
void opening.then(closeCaptureSession2, () => {
|
|
4662
|
+
});
|
|
4663
|
+
throw error;
|
|
4664
|
+
}
|
|
4665
|
+
};
|
|
4666
|
+
const session = probeSession ?? await openSession();
|
|
4617
4667
|
if (probeSession) {
|
|
4618
4668
|
openSegmentedSessionReuse(input, probeSession, injector);
|
|
4619
4669
|
probeSession = null;
|
|
4620
4670
|
}
|
|
4621
4671
|
try {
|
|
4622
|
-
if (!session.isInitialized) await initializeSession2(session);
|
|
4623
|
-
await completeDeferredDrawElementInit2(session);
|
|
4672
|
+
if (!session.isInitialized) await withinInitBudget(initializeSession2(session), session);
|
|
4673
|
+
await withinInitBudget(completeDeferredDrawElementInit2(session), session);
|
|
4624
4674
|
} catch (error) {
|
|
4625
4675
|
await closeCaptureSession2(session).catch(() => {
|
|
4626
4676
|
});
|
|
@@ -5552,7 +5602,7 @@ function trackSegmentFramePeak(frames, rootDir, onPeak) {
|
|
|
5552
5602
|
|
|
5553
5603
|
// src/services/render/segmentManifest.ts
|
|
5554
5604
|
import { createHash as createHash2 } from "node:crypto";
|
|
5555
|
-
import { existsSync as
|
|
5605
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5556
5606
|
import { join as join15 } from "node:path";
|
|
5557
5607
|
import { renameWithRetry as renameWithRetry2 } from "@hyperframes/engine";
|
|
5558
5608
|
var MANIFEST_FILENAME = "segments.json";
|
|
@@ -5600,7 +5650,7 @@ function isManifest(value) {
|
|
|
5600
5650
|
}
|
|
5601
5651
|
function readSegmentManifest(segmentDir) {
|
|
5602
5652
|
const path = join15(segmentDir, MANIFEST_FILENAME);
|
|
5603
|
-
if (!
|
|
5653
|
+
if (!existsSync8(path)) return null;
|
|
5604
5654
|
try {
|
|
5605
5655
|
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
5606
5656
|
return isManifest(parsed) ? parsed : null;
|
|
@@ -5629,7 +5679,7 @@ async function validateCompletedSegments(manifest, expectedHash, probeFrames) {
|
|
|
5629
5679
|
const ok = /* @__PURE__ */ new Set();
|
|
5630
5680
|
if (manifest.planHash !== expectedHash) return ok;
|
|
5631
5681
|
for (const entry of manifest.completed) {
|
|
5632
|
-
if (!
|
|
5682
|
+
if (!existsSync8(entry.path)) continue;
|
|
5633
5683
|
const size = statSync5(entry.path).size;
|
|
5634
5684
|
if (size <= 0 || size !== entry.bytes) continue;
|
|
5635
5685
|
const frames = await probeFrames(entry.path);
|
|
@@ -5769,7 +5819,7 @@ var ALLOWED_STRING_DATA_KEYS = /* @__PURE__ */ new Set([
|
|
|
5769
5819
|
"stagePhase",
|
|
5770
5820
|
// Full-fidelity fast-capture fallback trigger — the specific reason
|
|
5771
5821
|
// drawElement gated off ("filter:blur", "filter:drop-shadow",
|
|
5772
|
-
// "backdrop-filter", "clip-path", "at_risk_timeline", "swiftshader", …).
|
|
5822
|
+
// "backdrop-filter", "clip-path", "at_risk_timeline", "swiftshader", "webgl_unavailable", …).
|
|
5773
5823
|
// Populated on the `capture_fallback_profile` checkpoint emitted by
|
|
5774
5824
|
// `fallbackCaptureProfile.ts` when the operator opts into
|
|
5775
5825
|
// `HF_PROFILE_FALLBACK_CAPTURE=true` so downstream metric consumers can
|
|
@@ -6208,17 +6258,17 @@ function countAuthoredTimedClips(html) {
|
|
|
6208
6258
|
}
|
|
6209
6259
|
|
|
6210
6260
|
// src/services/render/stages/compileStage.ts
|
|
6211
|
-
import { existsSync as
|
|
6261
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6212
6262
|
import { join as join19 } from "node:path";
|
|
6213
6263
|
import { suggestMatchingPreset } from "@hyperframes/core";
|
|
6214
6264
|
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
6215
6265
|
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
6216
6266
|
|
|
6217
6267
|
// src/services/htmlCompiler.ts
|
|
6218
|
-
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
6268
|
+
import { decodedUrlPath as decodedUrlPath2 } from "@hyperframes/parsers/asset-paths";
|
|
6219
6269
|
import { createHash as createHash6 } from "node:crypto";
|
|
6220
|
-
import { createReadStream as createReadStream2, existsSync as
|
|
6221
|
-
import { join as join18, dirname as dirname8, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
|
|
6270
|
+
import { createReadStream as createReadStream2, existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync7 } from "fs";
|
|
6271
|
+
import { join as join18, dirname as dirname8, resolve as resolve7, basename as basename4, relative as relative2, sep as sep2 } from "path";
|
|
6222
6272
|
import { parseHTML as parseHTML5 } from "linkedom";
|
|
6223
6273
|
import {
|
|
6224
6274
|
COMPOSITION_SOURCE_URL,
|
|
@@ -6464,7 +6514,7 @@ import {
|
|
|
6464
6514
|
|
|
6465
6515
|
// src/services/animatedGifPrep.ts
|
|
6466
6516
|
import { createHash as createHash4 } from "node:crypto";
|
|
6467
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
6517
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, rmSync as rmSync9, statSync as statSync6 } from "node:fs";
|
|
6468
6518
|
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join16, resolve as resolve5 } from "node:path";
|
|
6469
6519
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
6470
6520
|
import { parseAnimatedGifMetadata } from "@hyperframes/core";
|
|
@@ -6503,14 +6553,14 @@ function resolveLocalImageSourcePath(src, options) {
|
|
|
6503
6553
|
const { basePath } = splitUrlSuffix(trimmed);
|
|
6504
6554
|
const normalizedBase = normalizeRelPath(basePath);
|
|
6505
6555
|
const mapped = options.sourceAssets?.get(trimmed) ?? options.sourceAssets?.get(basePath) ?? options.sourceAssets?.get(normalizedBase);
|
|
6506
|
-
if (mapped &&
|
|
6556
|
+
if (mapped && existsSync9(mapped)) return mapped;
|
|
6507
6557
|
if (isHttpUrl(trimmed)) return null;
|
|
6508
6558
|
const projectRelative = basePath.startsWith("/") ? basePath.slice(1) : basePath;
|
|
6509
6559
|
const candidates = [
|
|
6510
6560
|
isAbsolute2(basePath) ? basePath : resolve5(options.projectDir, projectRelative),
|
|
6511
6561
|
resolve5(options.downloadDir, normalizedBase)
|
|
6512
6562
|
];
|
|
6513
|
-
return candidates.find((candidate) =>
|
|
6563
|
+
return candidates.find((candidate) => existsSync9(candidate)) ?? null;
|
|
6514
6564
|
}
|
|
6515
6565
|
function isUsableFile(path) {
|
|
6516
6566
|
try {
|
|
@@ -6621,10 +6671,10 @@ async function runAnimatedGifTranscode(request) {
|
|
|
6621
6671
|
throw result.error ?? new Error(`Animated GIF transcode failed (${result.exitCode}): ${result.stderr.slice(-500)}`);
|
|
6622
6672
|
}
|
|
6623
6673
|
async function ensurePreparedWebm(input) {
|
|
6624
|
-
if (!
|
|
6674
|
+
if (!existsSync9(dirname6(input.cachePath))) {
|
|
6625
6675
|
mkdirSync7(dirname6(input.cachePath), { recursive: true });
|
|
6626
6676
|
}
|
|
6627
|
-
if (!
|
|
6677
|
+
if (!existsSync9(dirname6(input.outputPath))) {
|
|
6628
6678
|
mkdirSync7(dirname6(input.outputPath), { recursive: true });
|
|
6629
6679
|
}
|
|
6630
6680
|
if (!isUsableFile(input.cachePath)) {
|
|
@@ -7099,7 +7149,7 @@ import { createStudioPositionSeekReapplyScript } from "@hyperframes/studio-serve
|
|
|
7099
7149
|
import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
|
|
7100
7150
|
|
|
7101
7151
|
// src/services/assetMediaType.ts
|
|
7102
|
-
import { existsSync as
|
|
7152
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
7103
7153
|
import {
|
|
7104
7154
|
fingerprintElementId,
|
|
7105
7155
|
isNotMediaPayload,
|
|
@@ -7172,7 +7222,7 @@ async function preflightCompositionAssetMediaTypes(input) {
|
|
|
7172
7222
|
input.projectDir,
|
|
7173
7223
|
input.compiledDir
|
|
7174
7224
|
);
|
|
7175
|
-
if (!
|
|
7225
|
+
if (!existsSync10(resolvedPath)) continue;
|
|
7176
7226
|
byPath.set(resolvedPath, [...byPath.get(resolvedPath) ?? [], reference]);
|
|
7177
7227
|
}
|
|
7178
7228
|
const mismatches = [];
|
|
@@ -7260,6 +7310,14 @@ Check that each file referenced by data-composition-src contains valid HTML with
|
|
|
7260
7310
|
this.problems = problems;
|
|
7261
7311
|
}
|
|
7262
7312
|
};
|
|
7313
|
+
function normalizeCompositionSrcs(html, projectDir) {
|
|
7314
|
+
return html.replace(/(\bdata-composition-src=)(["'])([^"']*)\2/g, (whole, name, quote, value) => {
|
|
7315
|
+
if (!value.includes("%") && !value.startsWith("/")) return whole;
|
|
7316
|
+
const file = resolveCompositionSrc(projectDir, value);
|
|
7317
|
+
if (file === null) return whole;
|
|
7318
|
+
return `${name}${quote}${relative2(projectDir, file).split(sep2).join("/")}${quote}`;
|
|
7319
|
+
});
|
|
7320
|
+
}
|
|
7263
7321
|
function assertSubCompositionsUsable(html, projectDir, visited = /* @__PURE__ */ new Set()) {
|
|
7264
7322
|
const { document: document2 } = parseHTML5(html);
|
|
7265
7323
|
const hosts = [...document2.querySelectorAll("[data-composition-src]")];
|
|
@@ -7268,7 +7326,11 @@ function assertSubCompositionsUsable(html, projectDir, visited = /* @__PURE__ */
|
|
|
7268
7326
|
const srcPath = el.getAttribute("data-composition-src");
|
|
7269
7327
|
if (!srcPath) continue;
|
|
7270
7328
|
if (isUnresolvedAssetPlaceholder(srcPath)) continue;
|
|
7271
|
-
const filePath =
|
|
7329
|
+
const filePath = resolveCompositionSrc(projectDir, srcPath);
|
|
7330
|
+
if (filePath === null) {
|
|
7331
|
+
problems.push({ srcPath, detail: "it is outside the project folder" });
|
|
7332
|
+
continue;
|
|
7333
|
+
}
|
|
7272
7334
|
if (visited.has(filePath)) continue;
|
|
7273
7335
|
const read = readProjectFile(filePath);
|
|
7274
7336
|
if (read.kind === "missing") {
|
|
@@ -7414,7 +7476,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
7414
7476
|
let filePath = src;
|
|
7415
7477
|
if (isHttpUrl(src)) {
|
|
7416
7478
|
const remoteDir = join18(downloadDir, REMOTE_MEDIA_SUBDIR);
|
|
7417
|
-
if (!
|
|
7479
|
+
if (!existsSync11(remoteDir)) mkdirSync9(remoteDir, { recursive: true });
|
|
7418
7480
|
try {
|
|
7419
7481
|
filePath = await downloadToTemp(src, remoteDir, void 0, void 0, void 0, {
|
|
7420
7482
|
onTelemetry: logRemoteDownloadTelemetry
|
|
@@ -7423,11 +7485,11 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
7423
7485
|
return { duration: null, resolvedPath: src };
|
|
7424
7486
|
}
|
|
7425
7487
|
} else {
|
|
7426
|
-
filePath =
|
|
7488
|
+
filePath = decodedUrlPath2(src);
|
|
7427
7489
|
if (!filePath.startsWith("/")) filePath = join18(baseDir, filePath);
|
|
7428
7490
|
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
7429
7491
|
}
|
|
7430
|
-
if (!isHttpUrl(filePath) && !
|
|
7492
|
+
if (!isHttpUrl(filePath) && !existsSync11(filePath)) {
|
|
7431
7493
|
return { duration: null, resolvedPath: filePath };
|
|
7432
7494
|
}
|
|
7433
7495
|
const withSrcContext = (error) => {
|
|
@@ -7568,7 +7630,8 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
7568
7630
|
for (const el of compEls) {
|
|
7569
7631
|
const srcPath = el.getAttribute("data-composition-src");
|
|
7570
7632
|
if (!srcPath) continue;
|
|
7571
|
-
const filePath =
|
|
7633
|
+
const filePath = resolveCompositionSrc(projectDir, srcPath);
|
|
7634
|
+
if (filePath === null) continue;
|
|
7572
7635
|
if (visited.has(filePath)) {
|
|
7573
7636
|
continue;
|
|
7574
7637
|
}
|
|
@@ -7576,7 +7639,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
7576
7639
|
if (read.kind !== "file") {
|
|
7577
7640
|
continue;
|
|
7578
7641
|
}
|
|
7579
|
-
const rawSubHtml = read.text;
|
|
7642
|
+
const rawSubHtml = normalizeCompositionSrcs(read.text, projectDir);
|
|
7580
7643
|
const nestedVisited = new Set(visited);
|
|
7581
7644
|
nestedVisited.add(filePath);
|
|
7582
7645
|
workItems.push({ srcPath, filePath, rawSubHtml, nestedVisited });
|
|
@@ -7764,8 +7827,9 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
7764
7827
|
resolveHtml: (srcPath) => {
|
|
7765
7828
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
7766
7829
|
if (!compHtml) {
|
|
7767
|
-
const
|
|
7768
|
-
|
|
7830
|
+
const filePath = resolveCompositionSrc(projectDir, srcPath);
|
|
7831
|
+
const read = filePath ? readProjectFile(filePath) : null;
|
|
7832
|
+
if (read?.kind === "file") {
|
|
7769
7833
|
compHtml = rewriteUnresolvableGsapToCdn(read.text, projectDir, srcPath);
|
|
7770
7834
|
}
|
|
7771
7835
|
}
|
|
@@ -7774,7 +7838,7 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
7774
7838
|
parseHtml: (htmlStr) => parseHTML5(htmlStr).document,
|
|
7775
7839
|
// Mirrors the preview bundler: a sub-composition's SIBLING assets resolve
|
|
7776
7840
|
// against its own directory, project-root refs stay as authored.
|
|
7777
|
-
assetExists: (path) =>
|
|
7841
|
+
assetExists: (path) => existsSync11(resolve7(projectDir, path)),
|
|
7778
7842
|
// Preserve the authored root wrapper as a child of the host, matching
|
|
7779
7843
|
// the preview bundler's shape (htmlBundler.ts's prepareFlattenedInnerRoot,
|
|
7780
7844
|
// which the runtime compositionLoader mirrors with its own copy for the
|
|
@@ -7965,7 +8029,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
7965
8029
|
if (isPathInside2(absPath, absProjectDir)) {
|
|
7966
8030
|
return null;
|
|
7967
8031
|
}
|
|
7968
|
-
if (!
|
|
8032
|
+
if (!existsSync11(absPath)) return null;
|
|
7969
8033
|
const safeKey = toExternalAssetKey(absPath);
|
|
7970
8034
|
externalAssets.set(safeKey, absPath);
|
|
7971
8035
|
return safeKey;
|
|
@@ -8015,7 +8079,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
|
|
|
8015
8079
|
var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
8016
8080
|
async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
|
|
8017
8081
|
if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
|
|
8018
|
-
if (!
|
|
8082
|
+
if (!existsSync11(remoteDir)) mkdirSync9(remoteDir, { recursive: true });
|
|
8019
8083
|
const urlToLocal = /* @__PURE__ */ new Map();
|
|
8020
8084
|
await Promise.all(
|
|
8021
8085
|
[...urlSet].map(async (url) => {
|
|
@@ -8197,14 +8261,14 @@ function projectStylesheetFontFamilies(html, projectDir) {
|
|
|
8197
8261
|
for (const [tag] of html.matchAll(/<link\b[^>]*\brel=["']stylesheet["'][^>]*>/gi)) {
|
|
8198
8262
|
const href = /\bhref=["']([^"']+)["']/i.exec(tag)?.[1];
|
|
8199
8263
|
if (!href || /^(?:[a-z][a-z\d+.-]*:|\/\/)/i.test(href)) continue;
|
|
8200
|
-
const sheet = resolve7(projectDir,
|
|
8201
|
-
if (!isPathInside2(sheet, projectDir) || !
|
|
8264
|
+
const sheet = resolve7(projectDir, decodedUrlPath2(href).replace(/^(?:\.?\/)+/, ""));
|
|
8265
|
+
if (!isPathInside2(sheet, projectDir) || !existsSync11(sheet)) continue;
|
|
8202
8266
|
for (const face of extractFontFaceBlocks(readFileSync7(sheet, "utf8"))) {
|
|
8203
8267
|
const family = /font-family\s*:\s*([^;}]+)/i.exec(face)?.[1]?.trim().replace(/^['"]|['"]$/g, "");
|
|
8204
8268
|
const shipsFile = [...face.matchAll(LOCAL_FONTFACE_URL_RE)].some(([, url = ""]) => {
|
|
8205
|
-
const path =
|
|
8269
|
+
const path = decodedUrlPath2(url);
|
|
8206
8270
|
const file = path.startsWith("/") ? resolve7(projectDir, `.${path}`) : resolve7(dirname8(sheet), path);
|
|
8207
|
-
return isPathInside2(file, projectDir) &&
|
|
8271
|
+
return isPathInside2(file, projectDir) && existsSync11(file);
|
|
8208
8272
|
});
|
|
8209
8273
|
if (family && shipsFile) families.push(family);
|
|
8210
8274
|
}
|
|
@@ -8342,7 +8406,7 @@ function renderGsapDist() {
|
|
|
8342
8406
|
}
|
|
8343
8407
|
function rewriteUnresolvableGsapToCdn(html, projectDir, compSrcPath) {
|
|
8344
8408
|
let gsapDist;
|
|
8345
|
-
const exists = (path) =>
|
|
8409
|
+
const exists = (path) => existsSync11(resolve7(projectDir, path));
|
|
8346
8410
|
return html.replace(
|
|
8347
8411
|
/(<script\b[^>]*\bsrc=["'])([^"']*gsap[^"']*\/dist\/([^"']+))(["'][^>]*>)/gi,
|
|
8348
8412
|
(full, prefix, src, file, suffix) => {
|
|
@@ -8359,7 +8423,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
8359
8423
|
const entryPath = relative2(projectDir, htmlPath).replace(/\\/g, "/");
|
|
8360
8424
|
if (!entryPath.includes("/")) return html;
|
|
8361
8425
|
const { document: document2 } = parseHTML5(html);
|
|
8362
|
-
const assetExists = (path) =>
|
|
8426
|
+
const assetExists = (path) => existsSync11(resolve7(projectDir, path));
|
|
8363
8427
|
rewriteAssetPaths(
|
|
8364
8428
|
document2.querySelectorAll("[src], [href], [srcset]"),
|
|
8365
8429
|
entryPath,
|
|
@@ -8381,7 +8445,10 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
8381
8445
|
}
|
|
8382
8446
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
8383
8447
|
const entryHtml = rebaseDirectEntryAssetPaths(readTextFile(htmlPath), projectDir, htmlPath);
|
|
8384
|
-
const rawHtml =
|
|
8448
|
+
const rawHtml = normalizeCompositionSrcs(
|
|
8449
|
+
rewriteUnresolvableGsapToCdn(entryHtml, projectDir),
|
|
8450
|
+
projectDir
|
|
8451
|
+
);
|
|
8385
8452
|
assertSubCompositionsUsable(rawHtml, projectDir);
|
|
8386
8453
|
const probes = /* @__PURE__ */ new Map();
|
|
8387
8454
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
@@ -8926,7 +8993,7 @@ function resolveEntryHtmlPath(input) {
|
|
|
8926
8993
|
const rawEntry = readTextFile2(htmlPath);
|
|
8927
8994
|
if (entryFile === "index.html" || !isTemplateEntry(rawEntry)) return htmlPath;
|
|
8928
8995
|
const projectIndexPath = join19(projectDir, "index.html");
|
|
8929
|
-
if (!
|
|
8996
|
+
if (!existsSync12(projectIndexPath)) {
|
|
8930
8997
|
throw new Error(
|
|
8931
8998
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
8932
8999
|
);
|
|
@@ -9792,7 +9859,7 @@ function validateDistributedDuration(input) {
|
|
|
9792
9859
|
}
|
|
9793
9860
|
|
|
9794
9861
|
// src/services/render/stages/extractVideosStage.ts
|
|
9795
|
-
import { existsSync as
|
|
9862
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
9796
9863
|
import { join as join21 } from "node:path";
|
|
9797
9864
|
import {
|
|
9798
9865
|
classifyVideoExtractionError,
|
|
@@ -10067,7 +10134,7 @@ async function runExtractVideosStage(input) {
|
|
|
10067
10134
|
const probeFailures = await Promise.all(
|
|
10068
10135
|
composition.videos.map(async (v) => {
|
|
10069
10136
|
const videoPath = resolveProjectRelativeSrc3(v.src, projectDir, compiledDir);
|
|
10070
|
-
if (!
|
|
10137
|
+
if (!existsSync13(videoPath)) return null;
|
|
10071
10138
|
try {
|
|
10072
10139
|
const attempted = extractionPolicy.maxTransientRetries === 0 ? { result: await extractMediaMetadata3(videoPath), retries: 0 } : await runVideoExtractionWithRetry2(() => extractMediaMetadata3(videoPath), {
|
|
10073
10140
|
signal: abortSignal,
|
|
@@ -10105,7 +10172,7 @@ async function runExtractVideosStage(input) {
|
|
|
10105
10172
|
const probed = await Promise.all(
|
|
10106
10173
|
composition.images.map(async (img) => {
|
|
10107
10174
|
const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
|
|
10108
|
-
if (!
|
|
10175
|
+
if (!existsSync13(imgPath)) return null;
|
|
10109
10176
|
const colorSpace = await probeColorSpaceSafely(imgPath, log);
|
|
10110
10177
|
if (isHdrColorSpace2(colorSpace)) {
|
|
10111
10178
|
nativeHdrImageIds.add(img.id);
|
|
@@ -10225,7 +10292,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
10225
10292
|
videos.map(async (video) => {
|
|
10226
10293
|
const path = isHttpUrl2(video.src) ? video.src : resolveProjectRelativeSrc3(video.src, projectDir, compiledDir);
|
|
10227
10294
|
const readFrom = urlByAbsentLocalPath?.get(path) ?? path;
|
|
10228
|
-
if (!isHttpUrl2(readFrom) && !
|
|
10295
|
+
if (!isHttpUrl2(readFrom) && !existsSync13(readFrom)) return null;
|
|
10229
10296
|
const metadata = await extractMediaMetadata3(readFrom);
|
|
10230
10297
|
const window2 = await resolveFinalFrameExtractionWindow2(
|
|
10231
10298
|
readFrom,
|
|
@@ -10245,7 +10312,7 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
10245
10312
|
}
|
|
10246
10313
|
|
|
10247
10314
|
// src/services/render/videoLayerPassthroughPlan.ts
|
|
10248
|
-
import { existsSync as
|
|
10315
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
10249
10316
|
import {
|
|
10250
10317
|
buildVideoLayerInput,
|
|
10251
10318
|
isHttpUrl as isHttpUrl3,
|
|
@@ -10313,7 +10380,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
10313
10380
|
const { video } = q;
|
|
10314
10381
|
if (isHttpUrl3(video.src)) return { reason: "remote_source" };
|
|
10315
10382
|
const path = resolveProjectRelativeSrc4(video.src, input.projectDir, input.compiledDir);
|
|
10316
|
-
if (!
|
|
10383
|
+
if (!existsSync14(path)) return { reason: "source_missing" };
|
|
10317
10384
|
const layout = await probeVideoLayout(input.page, video.id, input.canvas, input.duration);
|
|
10318
10385
|
if ("reason" in layout) return layout;
|
|
10319
10386
|
const layer = await buildVideoLayerInput(
|
|
@@ -10328,7 +10395,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
10328
10395
|
}
|
|
10329
10396
|
|
|
10330
10397
|
// src/services/render/stages/audioStage.ts
|
|
10331
|
-
import { existsSync as
|
|
10398
|
+
import { existsSync as existsSync16 } from "node:fs";
|
|
10332
10399
|
import { join as join24 } from "node:path";
|
|
10333
10400
|
import {
|
|
10334
10401
|
MIXED_AUDIO_FILENAME,
|
|
@@ -10339,7 +10406,7 @@ import {
|
|
|
10339
10406
|
} from "@hyperframes/engine";
|
|
10340
10407
|
|
|
10341
10408
|
// src/services/render/audioDeliveryLimit.ts
|
|
10342
|
-
import { existsSync as
|
|
10409
|
+
import { existsSync as existsSync15 } from "node:fs";
|
|
10343
10410
|
import { dirname as dirname10, join as join23 } from "node:path";
|
|
10344
10411
|
import {
|
|
10345
10412
|
applyAudioFxChain,
|
|
@@ -10848,7 +10915,7 @@ async function limitDeliveryPeaks(input, deps = {
|
|
|
10848
10915
|
applyChain: applyAudioFxChain,
|
|
10849
10916
|
encode: encodeAac2,
|
|
10850
10917
|
rename: renameWithRetry6,
|
|
10851
|
-
sourceExists:
|
|
10918
|
+
sourceExists: existsSync15
|
|
10852
10919
|
}) {
|
|
10853
10920
|
const { audioPath, sourcePath, signal, timeoutMs } = input;
|
|
10854
10921
|
if (!deps.sourceExists(sourcePath)) return { success: true };
|
|
@@ -10943,7 +11010,7 @@ async function runAudioStageScoped(input) {
|
|
|
10943
11010
|
);
|
|
10944
11011
|
audioLoweredDb = guard.loweredDb;
|
|
10945
11012
|
input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
|
|
10946
|
-
if (input.keepDeliverySource && !guard.sourceStale &&
|
|
11013
|
+
if (input.keepDeliverySource && !guard.sourceStale && existsSync16(pcmSourcePath)) {
|
|
10947
11014
|
deliverySourcePath = pcmSourcePath;
|
|
10948
11015
|
}
|
|
10949
11016
|
}
|
|
@@ -11071,7 +11138,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
11071
11138
|
}
|
|
11072
11139
|
|
|
11073
11140
|
// src/services/render/stages/captureStage.ts
|
|
11074
|
-
import { existsSync as
|
|
11141
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4, statfsSync as statfsSync2 } from "node:fs";
|
|
11075
11142
|
import { join as join25 } from "node:path";
|
|
11076
11143
|
import {
|
|
11077
11144
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
@@ -11142,7 +11209,7 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
|
|
|
11142
11209
|
}
|
|
11143
11210
|
function measureCaptureFrameBytes(workDir, framesDir) {
|
|
11144
11211
|
let total = sampleDirectoryBytes(framesDir);
|
|
11145
|
-
if (!
|
|
11212
|
+
if (!existsSync17(workDir)) return total;
|
|
11146
11213
|
for (const attempt of readdirSync4(workDir)) {
|
|
11147
11214
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
11148
11215
|
total += sampleDirectoryBytes(join25(workDir, attempt));
|
|
@@ -11327,7 +11394,11 @@ async function runSequentialDiskCapture(input, captureOptions, captureCfg, check
|
|
|
11327
11394
|
prepareCaptureSessionForReuse3(session, framesDir, videoInjector);
|
|
11328
11395
|
probeSession = null;
|
|
11329
11396
|
}
|
|
11330
|
-
await ensureSessionReady(session
|
|
11397
|
+
await ensureSessionReady(session, {
|
|
11398
|
+
frameIndex: resumeFrom,
|
|
11399
|
+
totalFrames,
|
|
11400
|
+
abortSignal: input.abortSignal
|
|
11401
|
+
});
|
|
11331
11402
|
assertAttemptActive();
|
|
11332
11403
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
11333
11404
|
await captureSessionFrames(session, {
|
|
@@ -11379,13 +11450,13 @@ function planTransientResume(error, ctx) {
|
|
|
11379
11450
|
if (!firstMissing) return null;
|
|
11380
11451
|
return isTransientCaptureRetryEligible(failure.kind, missing, ctx.retriesUsed) ? firstMissing.startFrame : null;
|
|
11381
11452
|
}
|
|
11382
|
-
async function ensureSessionReady(session) {
|
|
11453
|
+
async function ensureSessionReady(session, budget) {
|
|
11383
11454
|
if (!session.isInitialized) {
|
|
11384
11455
|
await initializeSession5(session);
|
|
11385
11456
|
return;
|
|
11386
11457
|
}
|
|
11387
11458
|
if (process.env.PRODUCER_EXPERIMENTAL_FAST_CAPTURE === "true") {
|
|
11388
|
-
await completeDeferredDrawElementInit3(session);
|
|
11459
|
+
await createSessionInitBudget(budget)(completeDeferredDrawElementInit3(session), session);
|
|
11389
11460
|
}
|
|
11390
11461
|
}
|
|
11391
11462
|
async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resumeFrom, checkDiskProjection }) {
|
|
@@ -11445,7 +11516,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
11445
11516
|
}
|
|
11446
11517
|
|
|
11447
11518
|
// src/services/render/stages/captureHdrStage.ts
|
|
11448
|
-
import { existsSync as
|
|
11519
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync10 } from "node:fs";
|
|
11449
11520
|
import { join as join29 } from "node:path";
|
|
11450
11521
|
import {
|
|
11451
11522
|
closeCaptureSession as closeCaptureSession7,
|
|
@@ -11691,7 +11762,7 @@ import { Worker } from "node:worker_threads";
|
|
|
11691
11762
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
11692
11763
|
import { dirname as dirname11, join as join27 } from "node:path";
|
|
11693
11764
|
import { createRequire } from "node:module";
|
|
11694
|
-
import { existsSync as
|
|
11765
|
+
import { existsSync as existsSync18 } from "node:fs";
|
|
11695
11766
|
import { cpus as cpus2 } from "node:os";
|
|
11696
11767
|
function resolveWorkerEntry(explicit) {
|
|
11697
11768
|
if (explicit && explicit.length > 0) {
|
|
@@ -11704,7 +11775,7 @@ function resolveWorkerEntry(explicit) {
|
|
|
11704
11775
|
}
|
|
11705
11776
|
const moduleDir = dirname11(fileURLToPath3(import.meta.url));
|
|
11706
11777
|
const jsPath = join27(moduleDir, "shaderTransitionWorker.js");
|
|
11707
|
-
if (
|
|
11778
|
+
if (existsSync18(jsPath)) return { path: jsPath, isTs: false };
|
|
11708
11779
|
const tsPath = join27(moduleDir, "shaderTransitionWorker.ts");
|
|
11709
11780
|
return { path: tsPath, isTs: true };
|
|
11710
11781
|
}
|
|
@@ -12265,7 +12336,7 @@ async function runCaptureHdrStage(input) {
|
|
|
12265
12336
|
}
|
|
12266
12337
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
12267
12338
|
const debugDumpDir = debugDumpEnabled ? join29(framesDir, "debug-composite") : null;
|
|
12268
|
-
if (debugDumpDir && !
|
|
12339
|
+
if (debugDumpDir && !existsSync19(debugDumpDir)) {
|
|
12269
12340
|
mkdirSync10(debugDumpDir, { recursive: true });
|
|
12270
12341
|
}
|
|
12271
12342
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
@@ -12422,7 +12493,7 @@ async function runCaptureHdrStage(input) {
|
|
|
12422
12493
|
// src/services/render/stages/encodeStage.ts
|
|
12423
12494
|
import {
|
|
12424
12495
|
copyFileSync as copyFileSync4,
|
|
12425
|
-
existsSync as
|
|
12496
|
+
existsSync as existsSync20,
|
|
12426
12497
|
mkdirSync as mkdirSync11,
|
|
12427
12498
|
readdirSync as readdirSync5,
|
|
12428
12499
|
readFileSync as readFileSync8,
|
|
@@ -12556,7 +12627,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
12556
12627
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
12557
12628
|
if (failure) return failure;
|
|
12558
12629
|
}
|
|
12559
|
-
const fileSize =
|
|
12630
|
+
const fileSize = existsSync20(outputPath) ? statSync8(outputPath).size : 0;
|
|
12560
12631
|
return {
|
|
12561
12632
|
success: true,
|
|
12562
12633
|
outputPath,
|
|
@@ -12717,7 +12788,7 @@ async function runEncodeStage(input) {
|
|
|
12717
12788
|
const stage5Start = Date.now();
|
|
12718
12789
|
if (isPngSequence) {
|
|
12719
12790
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
12720
|
-
if (!
|
|
12791
|
+
if (!existsSync20(outputPath)) mkdirSync11(outputPath, { recursive: true });
|
|
12721
12792
|
const captured = readdirSync5(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
12722
12793
|
if (captured.length === 0) {
|
|
12723
12794
|
throw new Error(
|
|
@@ -12728,7 +12799,7 @@ async function runEncodeStage(input) {
|
|
|
12728
12799
|
const dst = join30(outputPath, formatExportFrameName(i, "png"));
|
|
12729
12800
|
copyFileSync4(join30(framesDir, name), dst);
|
|
12730
12801
|
});
|
|
12731
|
-
if (hasAudio && audioOutputPath &&
|
|
12802
|
+
if (hasAudio && audioOutputPath && existsSync20(audioOutputPath)) {
|
|
12732
12803
|
copyFileSync4(audioOutputPath, join30(outputPath, MIXED_AUDIO_FILENAME2));
|
|
12733
12804
|
log.info(
|
|
12734
12805
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
@@ -13117,7 +13188,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
13117
13188
|
let rangeStart = null;
|
|
13118
13189
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
13119
13190
|
const framePath = join31(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
13120
|
-
const missing = !
|
|
13191
|
+
const missing = !existsSync21(framePath) || statSync9(framePath).size <= 8;
|
|
13121
13192
|
if (missing && rangeStart === null) {
|
|
13122
13193
|
rangeStart = frameIndex;
|
|
13123
13194
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -13242,7 +13313,7 @@ function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
|
13242
13313
|
let captured = 0;
|
|
13243
13314
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
13244
13315
|
const framePath = join31(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
13245
|
-
if (
|
|
13316
|
+
if (existsSync21(framePath)) captured++;
|
|
13246
13317
|
}
|
|
13247
13318
|
return captured;
|
|
13248
13319
|
}
|
|
@@ -13907,7 +13978,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
13907
13978
|
validateDeliveryEncoderConfig(job.config);
|
|
13908
13979
|
const debugDir = resolveRenderDebugDir();
|
|
13909
13980
|
const outputDir = dirname13(outputPath);
|
|
13910
|
-
if (!
|
|
13981
|
+
if (!existsSync21(outputDir)) mkdirSync12(outputDir, { recursive: true });
|
|
13911
13982
|
const workDir = job.config.debug ? join31(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
13912
13983
|
const pipelineStart = Date.now();
|
|
13913
13984
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
@@ -14049,7 +14120,7 @@ async function executeRenderPipeline(input) {
|
|
|
14049
14120
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
14050
14121
|
assertNotAborted();
|
|
14051
14122
|
assertConfiguredFfmpegBinariesExist();
|
|
14052
|
-
if (!
|
|
14123
|
+
if (!existsSync21(workDir)) mkdirSync12(workDir, { recursive: true });
|
|
14053
14124
|
if (job.config.debug) {
|
|
14054
14125
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
14055
14126
|
}
|
|
@@ -14079,7 +14150,7 @@ async function executeRenderPipeline(input) {
|
|
|
14079
14150
|
});
|
|
14080
14151
|
const entryFile = job.config.entryFile || "index.html";
|
|
14081
14152
|
const htmlPath = join31(projectDir, entryFile);
|
|
14082
|
-
if (!
|
|
14153
|
+
if (!existsSync21(htmlPath)) {
|
|
14083
14154
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
14084
14155
|
}
|
|
14085
14156
|
assertNotAborted();
|
|
@@ -14394,9 +14465,11 @@ async function executeRenderPipeline(input) {
|
|
|
14394
14465
|
);
|
|
14395
14466
|
pendingAudio = audioStage.catch(() => void 0);
|
|
14396
14467
|
const stage4Start = Date.now();
|
|
14397
|
-
|
|
14468
|
+
const EFFECT_SCAN_STAGE = "Checking effects";
|
|
14469
|
+
const startingFrameCapture = () => updateJobStatus(job, "rendering", "Starting frame capture", 25, onProgress, {
|
|
14398
14470
|
code: "start_browsers"
|
|
14399
14471
|
});
|
|
14472
|
+
startingFrameCapture();
|
|
14400
14473
|
if (!fileServer) {
|
|
14401
14474
|
const fileServerStart = observability.stageStart("file_server", { reused: false });
|
|
14402
14475
|
try {
|
|
@@ -14421,7 +14494,7 @@ async function executeRenderPipeline(input) {
|
|
|
14421
14494
|
throw new Error("File server failed to initialize before frame capture");
|
|
14422
14495
|
}
|
|
14423
14496
|
const framesDir = join31(workDir, "captured-frames");
|
|
14424
|
-
if (!
|
|
14497
|
+
if (!existsSync21(framesDir)) mkdirSync12(framesDir, { recursive: true });
|
|
14425
14498
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
14426
14499
|
code: "start_browsers"
|
|
14427
14500
|
});
|
|
@@ -14429,6 +14502,7 @@ async function executeRenderPipeline(input) {
|
|
|
14429
14502
|
chromePath: resolveHeadlessShellPath(cfg),
|
|
14430
14503
|
browserTimeout: cfg.browserTimeout
|
|
14431
14504
|
});
|
|
14505
|
+
startingFrameCapture();
|
|
14432
14506
|
captureForceScreenshot = applyConcreteGpuScreenshotClamp(
|
|
14433
14507
|
captureForceScreenshot,
|
|
14434
14508
|
resolvedBrowserGpuMode,
|
|
@@ -14477,10 +14551,13 @@ async function executeRenderPipeline(input) {
|
|
|
14477
14551
|
onEffectScanProgress: (scanned, total) => updateJobStatus(
|
|
14478
14552
|
job,
|
|
14479
14553
|
"rendering",
|
|
14480
|
-
|
|
14554
|
+
`${EFFECT_SCAN_STAGE}, ${scanned} of ${total} frames`,
|
|
14481
14555
|
25,
|
|
14482
14556
|
onProgress
|
|
14483
14557
|
),
|
|
14558
|
+
onEffectScanEnd: () => {
|
|
14559
|
+
if (job.currentStage.startsWith(EFFECT_SCAN_STAGE)) startingFrameCapture();
|
|
14560
|
+
},
|
|
14484
14561
|
requiresWebGpu: compositionRequiresWebGpu2(compiled.html),
|
|
14485
14562
|
// Live route for Chrome memory (spec §5 Phase −1). The aggregate route
|
|
14486
14563
|
// through CapturePerfSummary only exists on success; a mid-capture
|
|
@@ -14748,6 +14825,7 @@ async function executeRenderPipeline(input) {
|
|
|
14748
14825
|
} finally {
|
|
14749
14826
|
calibrationFrames?.release();
|
|
14750
14827
|
}
|
|
14828
|
+
startingFrameCapture();
|
|
14751
14829
|
captureCalibration = outcome.calibration;
|
|
14752
14830
|
calibrationEvaluateTimedOut = outcome.calibrationEvaluateTimedOut;
|
|
14753
14831
|
captureForceScreenshot = outcome.forceScreenshot;
|
|
@@ -15806,7 +15884,7 @@ async function executeRenderPipeline(input) {
|
|
|
15806
15884
|
})
|
|
15807
15885
|
);
|
|
15808
15886
|
const totalElapsed = Date.now() - pipelineStart;
|
|
15809
|
-
const tmpPeakBytes =
|
|
15887
|
+
const tmpPeakBytes = existsSync21(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
15810
15888
|
recordTransientRetryObservability();
|
|
15811
15889
|
observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
|
|
15812
15890
|
const observabilitySummary = observability.summary({
|
|
@@ -15887,7 +15965,7 @@ async function executeRenderPipeline(input) {
|
|
|
15887
15965
|
}
|
|
15888
15966
|
}
|
|
15889
15967
|
if (job.config.debug) {
|
|
15890
|
-
if (!isPngSequence && !isHls &&
|
|
15968
|
+
if (!isPngSequence && !isHls && existsSync21(stagedOutputPath)) {
|
|
15891
15969
|
const debugOutput = join31(workDir, `output${videoExt}`);
|
|
15892
15970
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
15893
15971
|
}
|
|
@@ -16399,13 +16477,13 @@ import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframe
|
|
|
16399
16477
|
import {
|
|
16400
16478
|
copyFileSync as copyFileSync6,
|
|
16401
16479
|
cpSync as cpSync2,
|
|
16402
|
-
existsSync as
|
|
16480
|
+
existsSync as existsSync26,
|
|
16403
16481
|
linkSync,
|
|
16404
16482
|
mkdirSync as mkdirSync16,
|
|
16405
16483
|
rmSync as rmSync14,
|
|
16406
16484
|
writeFileSync as writeFileSync10
|
|
16407
16485
|
} from "node:fs";
|
|
16408
|
-
import { extname as extname6, isAbsolute as isAbsolute3, join as join40, relative as relative5, sep as
|
|
16486
|
+
import { extname as extname6, isAbsolute as isAbsolute3, join as join40, relative as relative5, sep as sep3 } from "node:path";
|
|
16409
16487
|
import {
|
|
16410
16488
|
fpsToNumber as fpsToNumber9,
|
|
16411
16489
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
@@ -16430,7 +16508,7 @@ import {
|
|
|
16430
16508
|
} from "@hyperframes/engine";
|
|
16431
16509
|
|
|
16432
16510
|
// src/services/distributed/chunkFrames.ts
|
|
16433
|
-
import { existsSync as
|
|
16511
|
+
import { existsSync as existsSync23, readdirSync as readdirSync7 } from "node:fs";
|
|
16434
16512
|
import { extname as extname4, join as join34 } from "node:path";
|
|
16435
16513
|
import {
|
|
16436
16514
|
compositionRequiresWebGpu as compositionRequiresWebGpu3,
|
|
@@ -16440,7 +16518,7 @@ import {
|
|
|
16440
16518
|
// src/services/distributed/shared.ts
|
|
16441
16519
|
import { execFile as execFileCallback } from "node:child_process";
|
|
16442
16520
|
import { dirname as dirname15, join as join33 } from "node:path";
|
|
16443
|
-
import { existsSync as
|
|
16521
|
+
import { existsSync as existsSync22, readFileSync as readFileSync9 } from "node:fs";
|
|
16444
16522
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
16445
16523
|
import { promisify as promisify2 } from "node:util";
|
|
16446
16524
|
import {
|
|
@@ -16456,7 +16534,7 @@ function isPlanAudioArtifactPath(path) {
|
|
|
16456
16534
|
function resolvePlanAudioPath(planDir) {
|
|
16457
16535
|
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
16458
16536
|
const candidate = join33(planDir, name);
|
|
16459
|
-
if (
|
|
16537
|
+
if (existsSync22(candidate)) return candidate;
|
|
16460
16538
|
}
|
|
16461
16539
|
return null;
|
|
16462
16540
|
}
|
|
@@ -16717,7 +16795,7 @@ function readProducerVersion() {
|
|
|
16717
16795
|
let current = startDir;
|
|
16718
16796
|
for (let i = 0; i < 10; i++) {
|
|
16719
16797
|
const candidate = join33(current, "package.json");
|
|
16720
|
-
if (
|
|
16798
|
+
if (existsSync22(candidate)) {
|
|
16721
16799
|
try {
|
|
16722
16800
|
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
16723
16801
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
@@ -16771,7 +16849,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
|
|
|
16771
16849
|
const result = [];
|
|
16772
16850
|
for (const v of videos) {
|
|
16773
16851
|
const outputDir = join34(planDir, "video-frames", v.videoId);
|
|
16774
|
-
if (!
|
|
16852
|
+
if (!existsSync23(outputDir)) {
|
|
16775
16853
|
throw new Error(
|
|
16776
16854
|
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
16777
16855
|
);
|
|
@@ -16870,7 +16948,7 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
16870
16948
|
const range = planned.get(video.videoId).deferredRange;
|
|
16871
16949
|
const remoteUrl = remoteSources[range.sourcePath];
|
|
16872
16950
|
const sourcePath = remoteUrl ?? join34(input.planDir, ...range.sourcePath.split("/"));
|
|
16873
|
-
if (remoteUrl === void 0 && !
|
|
16951
|
+
if (remoteUrl === void 0 && !existsSync23(sourcePath)) {
|
|
16874
16952
|
throw new RenderChunkValidationError(
|
|
16875
16953
|
MISSING_PLAN_ARTIFACT,
|
|
16876
16954
|
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
@@ -17070,7 +17148,7 @@ async function readPlanReferenceBackground(input) {
|
|
|
17070
17148
|
}
|
|
17071
17149
|
|
|
17072
17150
|
// src/services/render/stages/freezePlan.ts
|
|
17073
|
-
import { existsSync as
|
|
17151
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync10, readdirSync as readdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
17074
17152
|
import { join as join37, relative as relative3, resolve as resolve9 } from "node:path";
|
|
17075
17153
|
import {
|
|
17076
17154
|
REFERENCE_BACKGROUND_FIELD,
|
|
@@ -17264,7 +17342,7 @@ function planFileSha256(absolutePath, digests) {
|
|
|
17264
17342
|
return sha256;
|
|
17265
17343
|
}
|
|
17266
17344
|
async function prehashPlanFiles(dir, digests) {
|
|
17267
|
-
if (!
|
|
17345
|
+
if (!existsSync24(dir)) return;
|
|
17268
17346
|
for (const { absolutePath } of listPlanFiles(dir)) {
|
|
17269
17347
|
if (!digests.has(absolutePath)) digests.set(absolutePath, await sha256FileAsync(absolutePath));
|
|
17270
17348
|
}
|
|
@@ -17334,15 +17412,15 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
17334
17412
|
}
|
|
17335
17413
|
function readPlanRemoteFiles(planDir) {
|
|
17336
17414
|
const path = join37(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
17337
|
-
return
|
|
17415
|
+
return existsSync24(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
17338
17416
|
}
|
|
17339
17417
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
17340
17418
|
const planJsonPath = join37(planDir, "plan.json");
|
|
17341
17419
|
const encoderJsonPath = join37(planDir, "meta", "encoder.json");
|
|
17342
|
-
if (!
|
|
17420
|
+
if (!existsSync24(planJsonPath)) {
|
|
17343
17421
|
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
17344
17422
|
}
|
|
17345
|
-
if (!
|
|
17423
|
+
if (!existsSync24(encoderJsonPath)) {
|
|
17346
17424
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
17347
17425
|
}
|
|
17348
17426
|
const planJson = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
@@ -17372,11 +17450,11 @@ async function freezePlan(input) {
|
|
|
17372
17450
|
hasAudio,
|
|
17373
17451
|
digests
|
|
17374
17452
|
} = input;
|
|
17375
|
-
if (!
|
|
17453
|
+
if (!existsSync24(planDir)) {
|
|
17376
17454
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
17377
17455
|
}
|
|
17378
17456
|
const metaDir = join37(planDir, "meta");
|
|
17379
|
-
if (!
|
|
17457
|
+
if (!existsSync24(metaDir)) mkdirSync14(metaDir, { recursive: true });
|
|
17380
17458
|
writeFileSync8(
|
|
17381
17459
|
join37(metaDir, "composition.json"),
|
|
17382
17460
|
`${JSON.stringify(composition, null, 2)}
|
|
@@ -17453,7 +17531,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
17453
17531
|
}
|
|
17454
17532
|
|
|
17455
17533
|
// src/services/distributed/chunkAudio.ts
|
|
17456
|
-
import { closeSync as closeSync6, existsSync as
|
|
17534
|
+
import { closeSync as closeSync6, existsSync as existsSync25, ftruncateSync, mkdirSync as mkdirSync15, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
|
|
17457
17535
|
import { writeFileSync as writeFileSync9, writeSync } from "node:fs";
|
|
17458
17536
|
import { join as join38 } from "node:path";
|
|
17459
17537
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
@@ -17559,7 +17637,7 @@ async function writeChunkAudioSlices(input) {
|
|
|
17559
17637
|
}
|
|
17560
17638
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
17561
17639
|
const layoutPath = join38(planDir, SLICE_LAYOUT);
|
|
17562
|
-
if (!
|
|
17640
|
+
if (!existsSync25(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
17563
17641
|
const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
|
|
17564
17642
|
const frame = readFileSync11(join38(planDir, SLICE_FRAME));
|
|
17565
17643
|
const fd = openSync6(scratchPath, "w");
|
|
@@ -18105,7 +18183,7 @@ function pathInside(root, path) {
|
|
|
18105
18183
|
function stableSourcePath(roots, srcPath) {
|
|
18106
18184
|
for (const [root, prefix] of roots) {
|
|
18107
18185
|
const rel = pathInside(root, srcPath);
|
|
18108
|
-
if (rel !== null) return [prefix, ...rel.split(
|
|
18186
|
+
if (rel !== null) return [prefix, ...rel.split(sep3)].filter(Boolean).join("/");
|
|
18109
18187
|
}
|
|
18110
18188
|
return srcPath;
|
|
18111
18189
|
}
|
|
@@ -18119,8 +18197,8 @@ function createVideoSourceShipper(input) {
|
|
|
18119
18197
|
let result = null;
|
|
18120
18198
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
18121
18199
|
const rel = pathInside(root, srcPath);
|
|
18122
|
-
if (rel !== null && (
|
|
18123
|
-
result = ["compiled", ...rel.split(
|
|
18200
|
+
if (rel !== null && (existsSync26(join40(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
18201
|
+
result = ["compiled", ...rel.split(sep3)].join("/");
|
|
18124
18202
|
break;
|
|
18125
18203
|
}
|
|
18126
18204
|
}
|
|
@@ -18147,7 +18225,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
18147
18225
|
useGpu: false,
|
|
18148
18226
|
browserGpuMode: "software"
|
|
18149
18227
|
});
|
|
18150
|
-
if (!
|
|
18228
|
+
if (!existsSync26(planDir)) mkdirSync16(planDir, { recursive: true });
|
|
18151
18229
|
const log = config.logger ?? defaultLogger;
|
|
18152
18230
|
const sizeLimitBytes = options.executionPlanSizeLimitBytes ?? config.planDirSizeLimitBytes ?? PLAN_DIR_SIZE_LIMIT_BYTES;
|
|
18153
18231
|
const abortSignal = config.abortSignal;
|
|
@@ -18177,7 +18255,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
18177
18255
|
});
|
|
18178
18256
|
const entryFile = config.entryFile ?? "index.html";
|
|
18179
18257
|
const htmlPath = join40(projectDir, entryFile);
|
|
18180
|
-
if (!
|
|
18258
|
+
if (!existsSync26(htmlPath)) {
|
|
18181
18259
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
18182
18260
|
}
|
|
18183
18261
|
const workDir = join40(planDir, ".plan-work");
|
|
@@ -18192,7 +18270,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
18192
18270
|
filter: (src) => {
|
|
18193
18271
|
const rel = relative5(projectDir, src);
|
|
18194
18272
|
if (rel === "" || rel.startsWith("..")) return true;
|
|
18195
|
-
const firstSegment = rel.split(
|
|
18273
|
+
const firstSegment = rel.split(sep3, 1)[0];
|
|
18196
18274
|
return firstSegment === void 0 || !PLAN_PROJECT_DIR_SKIP_SEGMENTS.has(firstSegment);
|
|
18197
18275
|
}
|
|
18198
18276
|
});
|
|
@@ -18405,15 +18483,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18405
18483
|
logPlanStage(log, "extract", stageStarted);
|
|
18406
18484
|
const stagedVideoFrames = join40(compiledDir, "__hyperframes_video_frames");
|
|
18407
18485
|
const videoFramesDst = join40(planDir, "video-frames");
|
|
18408
|
-
if (
|
|
18486
|
+
if (existsSync26(videoFramesDst)) rmSync14(videoFramesDst, { recursive: true, force: true });
|
|
18409
18487
|
const videoSourcesDir = join40(planDir, "video-sources");
|
|
18410
18488
|
rmSync14(videoSourcesDir, { recursive: true, force: true });
|
|
18411
|
-
if (
|
|
18489
|
+
if (existsSync26(stagedVideoFrames)) {
|
|
18412
18490
|
renameWithRetry8(stagedVideoFrames, videoFramesDst);
|
|
18413
18491
|
} else {
|
|
18414
18492
|
mkdirSync16(videoFramesDst, { recursive: true });
|
|
18415
18493
|
}
|
|
18416
|
-
if (
|
|
18494
|
+
if (existsSync26(finalCompiledDir)) rmSync14(finalCompiledDir, { recursive: true, force: true });
|
|
18417
18495
|
renameWithRetry8(compiledDir, finalCompiledDir);
|
|
18418
18496
|
const shipVideoSource = createVideoSourceShipper({
|
|
18419
18497
|
planDir,
|
|
@@ -18533,10 +18611,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18533
18611
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
18534
18612
|
}
|
|
18535
18613
|
const planAudioPath = join40(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
18536
|
-
if (audioResult?.hasAudio &&
|
|
18614
|
+
if (audioResult?.hasAudio && existsSync26(audioResult.audioOutputPath)) {
|
|
18537
18615
|
renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
|
|
18538
18616
|
}
|
|
18539
|
-
if (chunksCarryAudio &&
|
|
18617
|
+
if (chunksCarryAudio && existsSync26(planAudioPath)) {
|
|
18540
18618
|
if (options.sliceChunkAudio) {
|
|
18541
18619
|
stageStarted = Date.now();
|
|
18542
18620
|
await writeChunkAudioSlices({
|
|
@@ -18654,7 +18732,7 @@ async function plan(projectDir, config, planDir, options = {}) {
|
|
|
18654
18732
|
|
|
18655
18733
|
// src/services/distributed/renderChunk.ts
|
|
18656
18734
|
import { randomBytes } from "node:crypto";
|
|
18657
|
-
import { existsSync as
|
|
18735
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync19, readFileSync as readFileSync13, readdirSync as readdirSync11, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
18658
18736
|
import { cpus as cpus3 } from "node:os";
|
|
18659
18737
|
import { join as join44 } from "node:path";
|
|
18660
18738
|
import {
|
|
@@ -18681,7 +18759,7 @@ import {
|
|
|
18681
18759
|
} from "@hyperframes/engine";
|
|
18682
18760
|
import {
|
|
18683
18761
|
copyFileSync as copyFileSync8,
|
|
18684
|
-
existsSync as
|
|
18762
|
+
existsSync as existsSync28,
|
|
18685
18763
|
lstatSync as lstatSync3,
|
|
18686
18764
|
mkdirSync as mkdirSync18,
|
|
18687
18765
|
mkdtempSync as mkdtempSync6,
|
|
@@ -18692,14 +18770,14 @@ import {
|
|
|
18692
18770
|
writeFileSync as writeFileSync12
|
|
18693
18771
|
} from "node:fs";
|
|
18694
18772
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
18695
|
-
import { dirname as dirname17, isAbsolute as isAbsolute4, join as join43, relative as relative6, resolve as resolve10, sep as
|
|
18773
|
+
import { dirname as dirname17, isAbsolute as isAbsolute4, join as join43, relative as relative6, resolve as resolve10, sep as sep4 } from "node:path";
|
|
18696
18774
|
import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
18697
18775
|
import { createFrameLookupTable as createFrameLookupTable5, renameWithRetry as renameWithRetry10 } from "@hyperframes/engine";
|
|
18698
18776
|
|
|
18699
18777
|
// src/services/distributed/planV2Publisher.ts
|
|
18700
18778
|
import {
|
|
18701
18779
|
copyFileSync as copyFileSync7,
|
|
18702
|
-
existsSync as
|
|
18780
|
+
existsSync as existsSync27,
|
|
18703
18781
|
linkSync as linkSync2,
|
|
18704
18782
|
mkdirSync as mkdirSync17,
|
|
18705
18783
|
mkdtempSync as mkdtempSync5,
|
|
@@ -18768,7 +18846,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18768
18846
|
#linkFile;
|
|
18769
18847
|
#committed = false;
|
|
18770
18848
|
constructor(destinationDir, options = {}) {
|
|
18771
|
-
if (
|
|
18849
|
+
if (existsSync27(destinationDir)) {
|
|
18772
18850
|
throw new PlanV2IntegrityError(`output directory already exists: ${destinationDir}`);
|
|
18773
18851
|
}
|
|
18774
18852
|
this.destinationDir = destinationDir;
|
|
@@ -18785,7 +18863,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18785
18863
|
);
|
|
18786
18864
|
}
|
|
18787
18865
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
18788
|
-
if (
|
|
18866
|
+
if (existsSync27(destinationPath)) return;
|
|
18789
18867
|
mkdirSync17(dirname16(destinationPath), { recursive: true });
|
|
18790
18868
|
const stagingDir = mkdtempSync5(join42(dirname16(destinationPath), ".plan-v2-blob-"));
|
|
18791
18869
|
const temporaryPath = join42(stagingDir, "blob");
|
|
@@ -18803,7 +18881,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18803
18881
|
}
|
|
18804
18882
|
async commitManifest(manifestBytes) {
|
|
18805
18883
|
for (const digest of new Set(manifestDigests(manifestBytes))) {
|
|
18806
|
-
if (!
|
|
18884
|
+
if (!existsSync27(planV2BlobPath(this.temporaryDir, digest))) {
|
|
18807
18885
|
throw new PlanV2IntegrityError(
|
|
18808
18886
|
`cannot commit manifest before referenced blob is durable: ${digest}`
|
|
18809
18887
|
);
|
|
@@ -18859,7 +18937,7 @@ function listFiles(root) {
|
|
|
18859
18937
|
walk(absolutePath);
|
|
18860
18938
|
} else if (entry.isFile()) {
|
|
18861
18939
|
files.push({
|
|
18862
|
-
path: relative6(rootResolved, absolutePath).split(
|
|
18940
|
+
path: relative6(rootResolved, absolutePath).split(sep4).join("/"),
|
|
18863
18941
|
absolutePath
|
|
18864
18942
|
});
|
|
18865
18943
|
} else {
|
|
@@ -18882,7 +18960,7 @@ function assertValidExtractionCacheCompleteSentinel(path) {
|
|
|
18882
18960
|
}
|
|
18883
18961
|
function validateExtractionCacheCompleteSentinels(executionPlanDir) {
|
|
18884
18962
|
const videoRoot = join43(executionPlanDir, "video-frames");
|
|
18885
|
-
if (!
|
|
18963
|
+
if (!existsSync28(videoRoot)) return;
|
|
18886
18964
|
for (const videoEntry of readdirSync10(videoRoot, { withFileTypes: true })) {
|
|
18887
18965
|
if (!videoEntry.isDirectory()) continue;
|
|
18888
18966
|
const videoDir = join43(videoRoot, videoEntry.name);
|
|
@@ -18899,7 +18977,7 @@ function isExtractionCacheCompleteSentinelPath(path) {
|
|
|
18899
18977
|
function resolveExtractedVideoOutputDir(planDir, videoId) {
|
|
18900
18978
|
const videoRoot = resolve10(planDir, "video-frames");
|
|
18901
18979
|
const outputDir = resolve10(videoRoot, videoId);
|
|
18902
|
-
if (outputDir === videoRoot || !outputDir.startsWith(`${videoRoot}${
|
|
18980
|
+
if (outputDir === videoRoot || !outputDir.startsWith(`${videoRoot}${sep4}`)) {
|
|
18903
18981
|
throw new PlanV2IntegrityError(`unsafe extracted video id: ${JSON.stringify(videoId)}`);
|
|
18904
18982
|
}
|
|
18905
18983
|
return outputDir;
|
|
@@ -18983,7 +19061,7 @@ function parsePlanVideosJson2(value) {
|
|
|
18983
19061
|
}
|
|
18984
19062
|
function materializeExtractedVideoDirectories(planDir) {
|
|
18985
19063
|
const videosPath = join43(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
18986
|
-
if (!
|
|
19064
|
+
if (!existsSync28(videosPath)) return;
|
|
18987
19065
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
18988
19066
|
for (const video of videos.extracted) {
|
|
18989
19067
|
mkdirSync18(resolveExtractedVideoOutputDir(planDir, video.videoId), { recursive: true });
|
|
@@ -19017,9 +19095,9 @@ function parseChunkSlices(value) {
|
|
|
19017
19095
|
}
|
|
19018
19096
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
19019
19097
|
const videoRoot = join43(executionPlanDir, "video-frames");
|
|
19020
|
-
const hasExtractedFrames =
|
|
19098
|
+
const hasExtractedFrames = existsSync28(videoRoot) && listFiles(videoRoot).length > 0;
|
|
19021
19099
|
const videosPath = join43(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19022
|
-
if (!
|
|
19100
|
+
if (!existsSync28(videosPath)) {
|
|
19023
19101
|
return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
|
|
19024
19102
|
}
|
|
19025
19103
|
const chunksPath = join43(executionPlanDir, "meta", "chunks.json");
|
|
@@ -19059,7 +19137,7 @@ function collectChunkVideoDependencies(input) {
|
|
|
19059
19137
|
for (let frame = chunk.startFrame; frame < chunk.endFrame; frame++) {
|
|
19060
19138
|
const globalTime = frame * input.fpsDen / input.fpsNum;
|
|
19061
19139
|
for (const [videoId, payload] of input.table.getActiveFramePayloads(globalTime)) {
|
|
19062
|
-
const path = input.deferredSources.get(videoId) ?? relative6(resolve10(input.executionPlanDir), payload.framePath).split(
|
|
19140
|
+
const path = input.deferredSources.get(videoId) ?? relative6(resolve10(input.executionPlanDir), payload.framePath).split(sep4).join("/");
|
|
19063
19141
|
assertSafeRelativePath(path);
|
|
19064
19142
|
const indexes = mutable.get(path) ?? /* @__PURE__ */ new Set();
|
|
19065
19143
|
indexes.add(chunk.index);
|
|
@@ -19076,7 +19154,7 @@ function computeManifestHash(manifest) {
|
|
|
19076
19154
|
return sha256Hex(`${PLAN_V2_HASH_PREFIX}${canonicalJsonStringify(manifestPayload(manifest))}`);
|
|
19077
19155
|
}
|
|
19078
19156
|
function writeBlob(sourcePath, destinationPath) {
|
|
19079
|
-
if (
|
|
19157
|
+
if (existsSync28(destinationPath)) return;
|
|
19080
19158
|
mkdirSync18(dirname17(destinationPath), { recursive: true });
|
|
19081
19159
|
const temporaryDir = mkdtempSync6(join43(dirname17(destinationPath), ".plan-v2-blob-"));
|
|
19082
19160
|
const temporaryPath = join43(temporaryDir, "blob");
|
|
@@ -19116,7 +19194,7 @@ async function putTasks(tasks) {
|
|
|
19116
19194
|
}
|
|
19117
19195
|
function buildPlanV2Publication(executionPlanDir, digests) {
|
|
19118
19196
|
const executionPlanPath = join43(executionPlanDir, "plan.json");
|
|
19119
|
-
if (!
|
|
19197
|
+
if (!existsSync28(executionPlanPath)) {
|
|
19120
19198
|
throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
|
|
19121
19199
|
}
|
|
19122
19200
|
const executionPlanValue = readJsonFile(executionPlanPath, "execution plan.json");
|
|
@@ -19196,7 +19274,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
19196
19274
|
};
|
|
19197
19275
|
}
|
|
19198
19276
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
19199
|
-
if (
|
|
19277
|
+
if (existsSync28(planV2Dir)) {
|
|
19200
19278
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
19201
19279
|
}
|
|
19202
19280
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
@@ -19282,7 +19360,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
19282
19360
|
}
|
|
19283
19361
|
}
|
|
19284
19362
|
async function planV2(projectDir, config, planV2Dir) {
|
|
19285
|
-
if (
|
|
19363
|
+
if (existsSync28(planV2Dir)) {
|
|
19286
19364
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
19287
19365
|
}
|
|
19288
19366
|
const publisher = new LocalPlanV2ArtifactPublisher(planV2Dir);
|
|
@@ -19436,7 +19514,7 @@ function parsePlanV2Manifest(value) {
|
|
|
19436
19514
|
}
|
|
19437
19515
|
function readPlanV2Manifest(planV2Dir) {
|
|
19438
19516
|
const manifestPath = join43(planV2Dir, "plan.json");
|
|
19439
|
-
if (!
|
|
19517
|
+
if (!existsSync28(manifestPath)) {
|
|
19440
19518
|
throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
|
|
19441
19519
|
}
|
|
19442
19520
|
const value = readJsonFile(manifestPath, "v2 manifest");
|
|
@@ -19455,7 +19533,7 @@ function listPlanV2ArtifactsForTarget(manifest, target) {
|
|
|
19455
19533
|
}
|
|
19456
19534
|
function verifyBlob(planV2Dir, artifact) {
|
|
19457
19535
|
const sourcePath = planV2BlobPath(planV2Dir, artifact.sha256);
|
|
19458
|
-
if (!
|
|
19536
|
+
if (!existsSync28(sourcePath)) {
|
|
19459
19537
|
throw new PlanV2IntegrityError(`missing content-addressed artifact ${artifact.sha256}`);
|
|
19460
19538
|
}
|
|
19461
19539
|
const stats = statSync11(sourcePath);
|
|
@@ -19468,7 +19546,7 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
19468
19546
|
return sourcePath;
|
|
19469
19547
|
}
|
|
19470
19548
|
function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
|
|
19471
|
-
if (
|
|
19549
|
+
if (existsSync28(destinationDir)) {
|
|
19472
19550
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
19473
19551
|
}
|
|
19474
19552
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
@@ -19533,7 +19611,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
19533
19611
|
}
|
|
19534
19612
|
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
19535
19613
|
const markerPath = join43(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
19536
|
-
if (!
|
|
19614
|
+
if (!existsSync28(markerPath)) return null;
|
|
19537
19615
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
19538
19616
|
if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
|
|
19539
19617
|
throw new PlanV2IntegrityError("malformed materialization marker");
|
|
@@ -19551,7 +19629,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
19551
19629
|
for (const artifact of required) {
|
|
19552
19630
|
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
19553
19631
|
const path = join43(planDir, ...artifact.path.split("/"));
|
|
19554
|
-
if (!
|
|
19632
|
+
if (!existsSync28(path) || statSync11(path).size !== artifact.sizeBytes) {
|
|
19555
19633
|
throw new PlanV2IntegrityError(
|
|
19556
19634
|
`materialized artifact missing or truncated: ${artifact.path}`
|
|
19557
19635
|
);
|
|
@@ -19651,12 +19729,23 @@ async function runCaptureWithScreenshotFallback(input) {
|
|
|
19651
19729
|
try {
|
|
19652
19730
|
return await input.run(input.forceScreenshot);
|
|
19653
19731
|
} catch (error) {
|
|
19654
|
-
if (input.forceScreenshot || await input.canRetry?.() === false)
|
|
19655
|
-
|
|
19732
|
+
if (input.forceScreenshot || await input.canRetry?.() === false)
|
|
19733
|
+
throw imageMissingIn(error) ?? error;
|
|
19734
|
+
if (!shouldRetryChunkCaptureWithScreenshot(error)) throw imageMissingIn(error) ?? error;
|
|
19656
19735
|
input.onFallback?.(error);
|
|
19657
19736
|
await input.resetForScreenshotRetry();
|
|
19658
|
-
return await input.run(true)
|
|
19737
|
+
return await input.run(true).catch((retryError) => {
|
|
19738
|
+
throw imageMissingIn(retryError) ?? retryError;
|
|
19739
|
+
});
|
|
19740
|
+
}
|
|
19741
|
+
}
|
|
19742
|
+
function imageMissingIn(error) {
|
|
19743
|
+
let e = error;
|
|
19744
|
+
for (let depth = 0; depth < 5 && typeof e === "object" && e !== null; depth++) {
|
|
19745
|
+
if (e.name === "ImageMissingFromFrameError") return e;
|
|
19746
|
+
e = e.cause;
|
|
19659
19747
|
}
|
|
19748
|
+
return void 0;
|
|
19660
19749
|
}
|
|
19661
19750
|
async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBeginFrameLiveness2) {
|
|
19662
19751
|
if (session.launchCaptureMode !== "beginframe") return false;
|
|
@@ -19691,7 +19780,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19691
19780
|
const planJsonPath = join44(planDir, "plan.json");
|
|
19692
19781
|
const encoderJsonPath = join44(planDir, "meta", "encoder.json");
|
|
19693
19782
|
const chunksJsonPath = join44(planDir, "meta", "chunks.json");
|
|
19694
|
-
if (!
|
|
19783
|
+
if (!existsSync29(planJsonPath)) {
|
|
19695
19784
|
throw new RenderChunkValidationError(
|
|
19696
19785
|
MISSING_PLAN_ARTIFACT,
|
|
19697
19786
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
@@ -19707,7 +19796,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19707
19796
|
const v2Manifest = v2Materialization?.manifest ?? null;
|
|
19708
19797
|
const remoteSources = v2Materialization?.remoteSources ?? {};
|
|
19709
19798
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
19710
|
-
if (!
|
|
19799
|
+
if (!existsSync29(required)) {
|
|
19711
19800
|
throw new RenderChunkValidationError(
|
|
19712
19801
|
MISSING_PLAN_ARTIFACT,
|
|
19713
19802
|
`[renderChunk] planDir is missing required artifact: ${required}`
|
|
@@ -19718,7 +19807,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19718
19807
|
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
19719
19808
|
const videosJsonPath = join44(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19720
19809
|
let planVideos = null;
|
|
19721
|
-
if (
|
|
19810
|
+
if (existsSync29(videosJsonPath)) {
|
|
19722
19811
|
try {
|
|
19723
19812
|
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync13(videosJsonPath, "utf-8")));
|
|
19724
19813
|
} catch (err) {
|
|
@@ -19749,7 +19838,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19749
19838
|
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
19750
19839
|
const framesInChunk = range.endFrame - range.startFrame;
|
|
19751
19840
|
const compiledDir = join44(planDir, "compiled");
|
|
19752
|
-
if (!
|
|
19841
|
+
if (!existsSync29(compiledDir)) {
|
|
19753
19842
|
throw new RenderChunkValidationError(
|
|
19754
19843
|
MISSING_PLAN_ARTIFACT,
|
|
19755
19844
|
`[renderChunk] planDir missing compiled/ directory: ${compiledDir}`
|
|
@@ -19999,10 +20088,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19999
20088
|
const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
|
|
20000
20089
|
const videoOnlyPath = encoder.chunksCarryAudio ? `${outputChunkPath}.video.mp4` : outputChunkPath;
|
|
20001
20090
|
if (isPngSequence) {
|
|
20002
|
-
if (!
|
|
20091
|
+
if (!existsSync29(outputChunkPath)) mkdirSync19(outputChunkPath, { recursive: true });
|
|
20003
20092
|
} else {
|
|
20004
20093
|
const outDir = join44(outputChunkPath, "..");
|
|
20005
|
-
if (!
|
|
20094
|
+
if (!existsSync29(outDir)) mkdirSync19(outDir, { recursive: true });
|
|
20006
20095
|
}
|
|
20007
20096
|
const encodeInput = {
|
|
20008
20097
|
job,
|
|
@@ -20902,7 +20991,7 @@ import {
|
|
|
20902
20991
|
|
|
20903
20992
|
// src/server.ts
|
|
20904
20993
|
import {
|
|
20905
|
-
existsSync as
|
|
20994
|
+
existsSync as existsSync32,
|
|
20906
20995
|
mkdirSync as mkdirSync21,
|
|
20907
20996
|
statSync as statSync13,
|
|
20908
20997
|
mkdtempSync as mkdtempSync8,
|
|
@@ -20925,7 +21014,7 @@ import {
|
|
|
20925
21014
|
|
|
20926
21015
|
// src/services/hyperframeLint.ts
|
|
20927
21016
|
import {
|
|
20928
|
-
existsSync as
|
|
21017
|
+
existsSync as existsSync30,
|
|
20929
21018
|
readFileSync as readFileSync15,
|
|
20930
21019
|
statSync as statSync12,
|
|
20931
21020
|
openSync as openSync7,
|
|
@@ -20933,7 +21022,7 @@ import {
|
|
|
20933
21022
|
closeSync as closeSync7,
|
|
20934
21023
|
constants as constants3
|
|
20935
21024
|
} from "node:fs";
|
|
20936
|
-
import { resolve as resolve11, join as join46, relative as relative7, isAbsolute as isAbsolute5, sep as
|
|
21025
|
+
import { resolve as resolve11, join as join46, relative as relative7, isAbsolute as isAbsolute5, sep as sep5 } from "node:path";
|
|
20937
21026
|
import { lintHyperframeHtml } from "@hyperframes/lint";
|
|
20938
21027
|
function isStringRecord(value) {
|
|
20939
21028
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -20979,7 +21068,7 @@ function readEntryHtml(filePath) {
|
|
|
20979
21068
|
}
|
|
20980
21069
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
20981
21070
|
const absProjectDir = resolve11(projectDir);
|
|
20982
|
-
if (!
|
|
21071
|
+
if (!existsSync30(absProjectDir) || !statSync12(absProjectDir).isDirectory()) {
|
|
20983
21072
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
20984
21073
|
}
|
|
20985
21074
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -20988,7 +21077,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
20988
21077
|
for (const entryFile of entryCandidates) {
|
|
20989
21078
|
const absoluteEntryPath = resolve11(absProjectDir, entryFile);
|
|
20990
21079
|
const entryRelativePath = relative7(absProjectDir, absoluteEntryPath);
|
|
20991
|
-
if (entryRelativePath === ".." || entryRelativePath.startsWith(`..${
|
|
21080
|
+
if (entryRelativePath === ".." || entryRelativePath.startsWith(`..${sep5}`) || isAbsolute5(entryRelativePath)) {
|
|
20992
21081
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
20993
21082
|
}
|
|
20994
21083
|
const html = readEntryHtml(absoluteEntryPath);
|
|
@@ -21048,7 +21137,7 @@ async function runHyperframeLint(prepared) {
|
|
|
21048
21137
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
21049
21138
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
21050
21139
|
import { dirname as dirname18, join as join47 } from "node:path";
|
|
21051
|
-
import { existsSync as
|
|
21140
|
+
import { existsSync as existsSync31 } from "node:fs";
|
|
21052
21141
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
21053
21142
|
async function startHealthWorker(options = {}) {
|
|
21054
21143
|
const log = options.logger ?? defaultLogger2();
|
|
@@ -21125,7 +21214,7 @@ function resolveWorkerEntry2() {
|
|
|
21125
21214
|
const here = dirname18(fileURLToPath6(import.meta.url));
|
|
21126
21215
|
const candidates = [join47(here, "healthWorkerThread.js"), join47(here, "healthWorkerThread.ts")];
|
|
21127
21216
|
for (const candidate of candidates) {
|
|
21128
|
-
if (
|
|
21217
|
+
if (existsSync31(candidate)) return candidate;
|
|
21129
21218
|
}
|
|
21130
21219
|
return void 0;
|
|
21131
21220
|
}
|
|
@@ -21271,7 +21360,7 @@ function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
|
21271
21360
|
const { input, cleanupProjectDir } = prepared;
|
|
21272
21361
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
21273
21362
|
const outputDir = dirname19(absoluteOutputPath);
|
|
21274
|
-
if (!
|
|
21363
|
+
if (!existsSync32(outputDir)) mkdirSync21(outputDir, { recursive: true });
|
|
21275
21364
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
21276
21365
|
}
|
|
21277
21366
|
function validateRenderOverrides(body) {
|
|
@@ -21312,11 +21401,11 @@ function prepareProjectDirectory(projectDir, options) {
|
|
|
21312
21401
|
const candidate = nonEmptyString(projectDir);
|
|
21313
21402
|
if (!candidate) return null;
|
|
21314
21403
|
const absProjectDir = resolve12(candidate);
|
|
21315
|
-
if (!
|
|
21404
|
+
if (!existsSync32(absProjectDir) || !statSync13(absProjectDir).isDirectory()) {
|
|
21316
21405
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
21317
21406
|
}
|
|
21318
21407
|
const entry = options.entryFile || "index.html";
|
|
21319
|
-
if (!
|
|
21408
|
+
if (!existsSync32(resolve12(absProjectDir, entry))) {
|
|
21320
21409
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
21321
21410
|
}
|
|
21322
21411
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -21408,7 +21497,7 @@ function cleanupTempDir(dir, log) {
|
|
|
21408
21497
|
}
|
|
21409
21498
|
}
|
|
21410
21499
|
function outputFileSize(path) {
|
|
21411
|
-
return
|
|
21500
|
+
return existsSync32(path) ? statSync13(path).size : 0;
|
|
21412
21501
|
}
|
|
21413
21502
|
function createBlockingProgressReporter(log, requestId) {
|
|
21414
21503
|
let lastLoggedPct = -10;
|
|
@@ -21712,7 +21801,7 @@ function createRenderHandlers(options = {}) {
|
|
|
21712
21801
|
if (!artifact) {
|
|
21713
21802
|
return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
21714
21803
|
}
|
|
21715
|
-
if (!
|
|
21804
|
+
if (!existsSync32(artifact.path)) {
|
|
21716
21805
|
store.delete(token);
|
|
21717
21806
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
21718
21807
|
}
|
|
@@ -21818,7 +21907,7 @@ import {
|
|
|
21818
21907
|
} from "@hyperframes/engine";
|
|
21819
21908
|
import {
|
|
21820
21909
|
cpSync as cpSync3,
|
|
21821
|
-
existsSync as
|
|
21910
|
+
existsSync as existsSync33,
|
|
21822
21911
|
mkdirSync as mkdirSync22,
|
|
21823
21912
|
mkdtempSync as mkdtempSync9,
|
|
21824
21913
|
readFileSync as readFileSync16,
|
|
@@ -21873,7 +21962,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21873
21962
|
}
|
|
21874
21963
|
const format = plan2.dimensions.format;
|
|
21875
21964
|
const cfrEncoder = cfr ? readCfrEncoder(planDir, format) : null;
|
|
21876
|
-
if (!
|
|
21965
|
+
if (!existsSync33(dirname20(outputPath))) {
|
|
21877
21966
|
mkdirSync22(dirname20(outputPath), { recursive: true });
|
|
21878
21967
|
}
|
|
21879
21968
|
const workDir = mkdtempSync9(`${outputPath}.assemble-work-`);
|
|
@@ -21897,7 +21986,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21897
21986
|
try {
|
|
21898
21987
|
const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
|
|
21899
21988
|
const finalPath = join49(workDir, `output.${format}`);
|
|
21900
|
-
const audio = audioPath !== null &&
|
|
21989
|
+
const audio = audioPath !== null && existsSync33(audioPath) ? audioPath : null;
|
|
21901
21990
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
21902
21991
|
renameWithRetry11(finalPath, outputPath);
|
|
21903
21992
|
log.info("[assemble] stage timings", {
|
|
@@ -21915,7 +22004,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21915
22004
|
});
|
|
21916
22005
|
}
|
|
21917
22006
|
}
|
|
21918
|
-
const fileSize =
|
|
22007
|
+
const fileSize = existsSync33(outputPath) ? statSync14(outputPath).size : 0;
|
|
21919
22008
|
return {
|
|
21920
22009
|
...referenceBackgroundResult8(plan2[REFERENCE_BACKGROUND_FIELD6]),
|
|
21921
22010
|
outputPath,
|
|
@@ -21927,13 +22016,13 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
21927
22016
|
function readAssemblePlan(planDir, chunkPaths) {
|
|
21928
22017
|
const planJsonPath = join49(planDir, "plan.json");
|
|
21929
22018
|
const chunksJsonPath = join49(planDir, "meta", "chunks.json");
|
|
21930
|
-
if (!
|
|
22019
|
+
if (!existsSync33(planJsonPath)) {
|
|
21931
22020
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
21932
22021
|
}
|
|
21933
22022
|
const plan2 = JSON.parse(readFileSync16(planJsonPath, "utf-8"));
|
|
21934
22023
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
21935
22024
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
21936
|
-
if (!
|
|
22025
|
+
if (!existsSync33(chunksJsonPath)) {
|
|
21937
22026
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
21938
22027
|
}
|
|
21939
22028
|
const chunks = JSON.parse(readFileSync16(chunksJsonPath, "utf-8"));
|
|
@@ -21948,7 +22037,7 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
21948
22037
|
}
|
|
21949
22038
|
}
|
|
21950
22039
|
for (const path of chunkPaths.flat()) {
|
|
21951
|
-
if (!
|
|
22040
|
+
if (!existsSync33(path)) {
|
|
21952
22041
|
throw new Error(`[assemble] chunk path does not exist: ${path}`);
|
|
21953
22042
|
}
|
|
21954
22043
|
}
|
|
@@ -22087,7 +22176,7 @@ function planFormat(planDir) {
|
|
|
22087
22176
|
}
|
|
22088
22177
|
function lockedAudio(planDir) {
|
|
22089
22178
|
const encoderJsonPath = join49(planDir, "meta", "encoder.json");
|
|
22090
|
-
if (!
|
|
22179
|
+
if (!existsSync33(encoderJsonPath)) return {};
|
|
22091
22180
|
return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
22092
22181
|
}
|
|
22093
22182
|
function readCfrEncoder(planDir, format) {
|
|
@@ -22097,7 +22186,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
22097
22186
|
);
|
|
22098
22187
|
}
|
|
22099
22188
|
const encoderJsonPath = join49(planDir, "meta", "encoder.json");
|
|
22100
|
-
if (!
|
|
22189
|
+
if (!existsSync33(encoderJsonPath)) {
|
|
22101
22190
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
22102
22191
|
}
|
|
22103
22192
|
const encoderJson = JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
@@ -22109,7 +22198,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
22109
22198
|
return encoderJson;
|
|
22110
22199
|
}
|
|
22111
22200
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
22112
|
-
if (
|
|
22201
|
+
if (existsSync33(outputPath)) rmSync20(outputPath, { recursive: true, force: true });
|
|
22113
22202
|
mkdirSync22(outputPath, { recursive: true });
|
|
22114
22203
|
let globalIdx = 0;
|
|
22115
22204
|
for (const chunkDir of chunkPaths) {
|
|
@@ -22133,7 +22222,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
22133
22222
|
`[assemble] png-sequence frame count mismatch: merged ${globalIdx} frames vs plan.totalFrames=${totalFrames}. Using on-disk count.`
|
|
22134
22223
|
);
|
|
22135
22224
|
}
|
|
22136
|
-
if (audioPath !== null &&
|
|
22225
|
+
if (audioPath !== null && existsSync33(audioPath)) {
|
|
22137
22226
|
const sidecar = join49(outputPath, MIXED_AUDIO_FILENAME4);
|
|
22138
22227
|
cpSync3(audioPath, sidecar);
|
|
22139
22228
|
}
|