@hyperframes/producer 0.9.26 → 0.9.28-alpha.0
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 +28 -23
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +67 -67
- package/dist/index.js +269 -266
- package/dist/public-server.js +131 -130
- package/dist/services/distributed/shared.d.ts +1 -0
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
referenceBackgroundResult,
|
|
68
68
|
createReferenceBackgroundRecorder
|
|
69
69
|
} from "@hyperframes/engine";
|
|
70
|
-
import { join as
|
|
70
|
+
import { join as join30, dirname as dirname13, resolve as resolve8 } from "path";
|
|
71
71
|
import { totalmem as totalmem2 } from "node:os";
|
|
72
72
|
import { randomUUID } from "crypto";
|
|
73
73
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -1557,7 +1557,7 @@ var RenderExecutionContext = class {
|
|
|
1557
1557
|
|
|
1558
1558
|
// src/services/render/artifactTransaction.ts
|
|
1559
1559
|
import { closeSync as closeSync2, existsSync as existsSync6, fstatSync as fstatSync2, openSync as openSync2, readSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
|
|
1560
|
-
import { basename as
|
|
1560
|
+
import { basename as basename4, dirname as dirname4, extname as extname2, join as join5, resolve as resolve4 } from "node:path";
|
|
1561
1561
|
import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, renameWithRetry } from "@hyperframes/engine";
|
|
1562
1562
|
|
|
1563
1563
|
// src/utils/ffprobe.ts
|
|
@@ -1571,7 +1571,6 @@ import {
|
|
|
1571
1571
|
import {
|
|
1572
1572
|
existsSync as existsSync5,
|
|
1573
1573
|
lstatSync,
|
|
1574
|
-
mkdtempSync,
|
|
1575
1574
|
readFileSync as readFileSync2,
|
|
1576
1575
|
readdirSync,
|
|
1577
1576
|
readlinkSync,
|
|
@@ -1579,8 +1578,9 @@ import {
|
|
|
1579
1578
|
writeFileSync as writeFileSync2
|
|
1580
1579
|
} from "node:fs";
|
|
1581
1580
|
import { hostname, uptime } from "node:os";
|
|
1582
|
-
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1581
|
+
import { basename as basename3, dirname as dirname3, join as join4 } from "node:path";
|
|
1583
1582
|
import { RENDER_TEMP_DIR } from "@hyperframes/core/project-folders";
|
|
1583
|
+
import { createStagingDir } from "@hyperframes/core/atomic-file";
|
|
1584
1584
|
var OWNER_FILE = "owner.json";
|
|
1585
1585
|
var TRANSACTION_BACKUP = "backup";
|
|
1586
1586
|
var RENDER_JOB_DIR = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/;
|
|
@@ -1600,7 +1600,7 @@ function pidScope() {
|
|
|
1600
1600
|
};
|
|
1601
1601
|
}
|
|
1602
1602
|
function createOwnedRenderDir(prefix) {
|
|
1603
|
-
const dir =
|
|
1603
|
+
const dir = createStagingDir(dirname3(prefix), basename3(prefix));
|
|
1604
1604
|
const owner = { pid: process.pid, ...pidScope() };
|
|
1605
1605
|
writeFileSync2(join4(dir, OWNER_FILE), JSON.stringify(owner));
|
|
1606
1606
|
return dir;
|
|
@@ -1710,7 +1710,7 @@ function buildArtifactExpectation(input) {
|
|
|
1710
1710
|
function createSiblingTransactionDirectory(destination) {
|
|
1711
1711
|
const parent = dirname4(destination);
|
|
1712
1712
|
const extension = extname2(destination);
|
|
1713
|
-
const stem = extension ?
|
|
1713
|
+
const stem = extension ? basename4(destination, extension) : basename4(destination);
|
|
1714
1714
|
return createOwnedRenderDir(join5(parent, `.${stem}.hf-transaction-`));
|
|
1715
1715
|
}
|
|
1716
1716
|
function assertReadableNonEmptyFile(path) {
|
|
@@ -1786,7 +1786,7 @@ var ArtifactTransaction = class {
|
|
|
1786
1786
|
this.logger = logger;
|
|
1787
1787
|
this.destinationPath = resolve4(destinationPath);
|
|
1788
1788
|
this.transactionDirectory = createSiblingTransactionDirectory(this.destinationPath);
|
|
1789
|
-
this.stagingPath = join5(this.transactionDirectory,
|
|
1789
|
+
this.stagingPath = join5(this.transactionDirectory, basename4(this.destinationPath));
|
|
1790
1790
|
this.backupPath = join5(this.transactionDirectory, TRANSACTION_BACKUP);
|
|
1791
1791
|
this.durationProbe = durationProbe;
|
|
1792
1792
|
}
|
|
@@ -1842,7 +1842,7 @@ var ArtifactTransaction = class {
|
|
|
1842
1842
|
}
|
|
1843
1843
|
if (expected.expectedFrames === void 0) return;
|
|
1844
1844
|
const frameCount = files.filter(
|
|
1845
|
-
(file) => dirname4(file) === this.stagingPath && /^frame_\d+\.png$/.test(
|
|
1845
|
+
(file) => dirname4(file) === this.stagingPath && /^frame_\d+\.png$/.test(basename4(file))
|
|
1846
1846
|
).length;
|
|
1847
1847
|
if (frameCount === 0) {
|
|
1848
1848
|
throw new Error(
|
|
@@ -2081,7 +2081,7 @@ function replanAfterFailure(plan2, failure) {
|
|
|
2081
2081
|
|
|
2082
2082
|
// src/services/render/stages/captureSegmentedStage.ts
|
|
2083
2083
|
import { existsSync as existsSync7, mkdirSync as mkdirSync3, rmSync as rmSync6, statSync as statSync3 } from "node:fs";
|
|
2084
|
-
import { join as
|
|
2084
|
+
import { join as join9 } from "node:path";
|
|
2085
2085
|
import {
|
|
2086
2086
|
captureFrameToBuffer as captureFrameToBuffer2,
|
|
2087
2087
|
closeCaptureSession as closeCaptureSession2,
|
|
@@ -2974,8 +2974,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
2974
2974
|
}
|
|
2975
2975
|
|
|
2976
2976
|
// src/services/render/stages/captureHdrResources.ts
|
|
2977
|
-
import { mkdirSync as mkdirSync2,
|
|
2978
|
-
import { join as join8 } from "node:path";
|
|
2977
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync5, statSync as statSync2, statfsSync } from "node:fs";
|
|
2979
2978
|
import {
|
|
2980
2979
|
decodePngToRgb48le,
|
|
2981
2980
|
extractMediaMetadata as extractMediaMetadata2,
|
|
@@ -3075,6 +3074,7 @@ function createHdrFrameWindows(opts) {
|
|
|
3075
3074
|
}
|
|
3076
3075
|
|
|
3077
3076
|
// src/services/render/stages/captureHdrResources.ts
|
|
3077
|
+
import { createStagingDir as createStagingDir2 } from "@hyperframes/core/atomic-file";
|
|
3078
3078
|
function tempDirSafePrefix(id) {
|
|
3079
3079
|
const safe = id.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
|
|
3080
3080
|
return safe || "video";
|
|
@@ -3332,7 +3332,7 @@ async function extractHdrVideoFrames(args) {
|
|
|
3332
3332
|
if (!video || !planned) continue;
|
|
3333
3333
|
const { window: window2, metadata } = planned;
|
|
3334
3334
|
mkdirSync2(framesDir, { recursive: true });
|
|
3335
|
-
const frameDir =
|
|
3335
|
+
const frameDir = createStagingDir2(framesDir, `hdr_${tempDirSafePrefix(videoId)}-`);
|
|
3336
3336
|
createdFrameDirs.add(frameDir);
|
|
3337
3337
|
const dims = prep.hdrExtractionDims.get(videoId) ?? { width, height };
|
|
3338
3338
|
const frameSize = dims.width * dims.height * 6;
|
|
@@ -3787,7 +3787,7 @@ function isTargetLossError(error) {
|
|
|
3787
3787
|
// src/services/render/stages/captureStreamingStage.ts
|
|
3788
3788
|
import { mkdtemp, writeFile } from "node:fs/promises";
|
|
3789
3789
|
import { tmpdir } from "node:os";
|
|
3790
|
-
import { join as
|
|
3790
|
+
import { join as join8 } from "node:path";
|
|
3791
3791
|
import {
|
|
3792
3792
|
DrawElementVerificationError,
|
|
3793
3793
|
captureFrameToBuffer,
|
|
@@ -4010,11 +4010,11 @@ async function verifyDrawElementSample(session, idx, buf, ctx) {
|
|
|
4010
4010
|
return;
|
|
4011
4011
|
}
|
|
4012
4012
|
if (db < verifyMinDb) {
|
|
4013
|
-
const dumpDir = await mkdtemp(
|
|
4013
|
+
const dumpDir = await mkdtemp(join8(tmpdir(), "hf-de-verify-fail-")).catch(() => null);
|
|
4014
4014
|
if (dumpDir) {
|
|
4015
4015
|
await Promise.all([
|
|
4016
|
-
writeFile(
|
|
4017
|
-
writeFile(
|
|
4016
|
+
writeFile(join8(dumpDir, `frame-${idx}-de.jpg`), buf),
|
|
4017
|
+
writeFile(join8(dumpDir, `frame-${idx}-truth.jpg`), truth)
|
|
4018
4018
|
]).catch(() => {
|
|
4019
4019
|
});
|
|
4020
4020
|
}
|
|
@@ -4585,7 +4585,7 @@ async function runCaptureStreamingStage(input) {
|
|
|
4585
4585
|
|
|
4586
4586
|
// src/services/render/stages/captureSegmentedStage.ts
|
|
4587
4587
|
function segmentOutputPath(segmentDir, index) {
|
|
4588
|
-
return
|
|
4588
|
+
return join9(segmentDir, `segment_${String(index).padStart(5, "0")}.mp4`);
|
|
4589
4589
|
}
|
|
4590
4590
|
function segmentEncoderOptions(base, segment) {
|
|
4591
4591
|
return {
|
|
@@ -4913,7 +4913,7 @@ async function runCaptureSegmentedStage(input) {
|
|
|
4913
4913
|
const { log, dedupPerfs, assertNotAborted } = input;
|
|
4914
4914
|
const deps = { ...DEFAULT_DEPS, ...input.deps };
|
|
4915
4915
|
const segments = planSegments(input.totalFrames, input.segmentFrames);
|
|
4916
|
-
const segmentDir = input.segmentDir ??
|
|
4916
|
+
const segmentDir = input.segmentDir ?? join9(input.workDir, "segments");
|
|
4917
4917
|
mkdirSync3(segmentDir, { recursive: true });
|
|
4918
4918
|
const skip = input.completedSegments ?? /* @__PURE__ */ new Set();
|
|
4919
4919
|
const pending = segments.filter((s) => !skip.has(s.index));
|
|
@@ -5060,7 +5060,7 @@ function startRenderEtaGuard(options) {
|
|
|
5060
5060
|
}
|
|
5061
5061
|
|
|
5062
5062
|
// src/services/render/captureCost.ts
|
|
5063
|
-
import { join as
|
|
5063
|
+
import { join as join10 } from "node:path";
|
|
5064
5064
|
import { fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
|
|
5065
5065
|
import {
|
|
5066
5066
|
calculateOptimalWorkers,
|
|
@@ -5306,7 +5306,7 @@ async function runCaptureCalibration(input) {
|
|
|
5306
5306
|
});
|
|
5307
5307
|
let calibration;
|
|
5308
5308
|
try {
|
|
5309
|
-
calibration = await runOneCalibration(
|
|
5309
|
+
calibration = await runOneCalibration(join10(workDir, "capture-calibration"), calibrationCfg);
|
|
5310
5310
|
} catch (error) {
|
|
5311
5311
|
const shouldFallback = !forceScreenshot && shouldFallbackToScreenshotAfterCalibrationError(error);
|
|
5312
5312
|
if (!shouldFallback) {
|
|
@@ -5340,7 +5340,7 @@ async function runCaptureCalibration(input) {
|
|
|
5340
5340
|
const screenshotCfg = createCaptureCalibrationConfig({ ...cfg, forceScreenshot: true });
|
|
5341
5341
|
try {
|
|
5342
5342
|
calibration = await runOneCalibration(
|
|
5343
|
-
|
|
5343
|
+
join10(workDir, "capture-calibration-screenshot"),
|
|
5344
5344
|
screenshotCfg
|
|
5345
5345
|
);
|
|
5346
5346
|
} catch (fallbackError) {
|
|
@@ -5405,7 +5405,7 @@ function checkPageNeverReady(input) {
|
|
|
5405
5405
|
|
|
5406
5406
|
// src/services/render/deferredVideoFrames.ts
|
|
5407
5407
|
import { mkdirSync as mkdirSync4, rmSync as rmSync7 } from "node:fs";
|
|
5408
|
-
import { dirname as dirname5, join as
|
|
5408
|
+
import { dirname as dirname5, join as join11 } from "node:path";
|
|
5409
5409
|
import {
|
|
5410
5410
|
createFrameLookupTable,
|
|
5411
5411
|
extractVideoFramesRange,
|
|
@@ -5449,7 +5449,7 @@ async function extractDeferredFrames(input) {
|
|
|
5449
5449
|
const shown = shownDeferredFrameIndexes(input, input.frames, input.fps);
|
|
5450
5450
|
return Promise.all(
|
|
5451
5451
|
deferred.map(async (video) => {
|
|
5452
|
-
const outputDir =
|
|
5452
|
+
const outputDir = join11(input.outputRoot, video.videoId);
|
|
5453
5453
|
const indexes = [...shown.get(video.videoId) ?? []].sort((a, b) => a - b);
|
|
5454
5454
|
if (indexes.length === 0) {
|
|
5455
5455
|
return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
|
|
@@ -5518,13 +5518,13 @@ async function extractAllDeferredFrames(extracted, input) {
|
|
|
5518
5518
|
|
|
5519
5519
|
// src/services/render/segmentVideoFrames.ts
|
|
5520
5520
|
import { mkdirSync as mkdirSync5, rmSync as rmSync8 } from "node:fs";
|
|
5521
|
-
import { join as
|
|
5521
|
+
import { join as join12 } from "node:path";
|
|
5522
5522
|
import {
|
|
5523
5523
|
createFrameLookupTable as createFrameLookupTable2,
|
|
5524
5524
|
createVideoFrameInjector
|
|
5525
5525
|
} from "@hyperframes/engine";
|
|
5526
5526
|
function segmentFramesDir(rootDir, index) {
|
|
5527
|
-
return
|
|
5527
|
+
return join12(rootDir, "segment-frames", `segment_${String(index).padStart(5, "0")}`);
|
|
5528
5528
|
}
|
|
5529
5529
|
function createSegmentVideoFrames(input) {
|
|
5530
5530
|
const extractFrames = input.extractFrames ?? extractDeferredFrames;
|
|
@@ -5554,7 +5554,7 @@ function createSegmentVideoFrames(input) {
|
|
|
5554
5554
|
let onDemandCalls = 0;
|
|
5555
5555
|
const onDemandHook = () => Object.assign(
|
|
5556
5556
|
async (page, time, heldVideoTime) => {
|
|
5557
|
-
const dir =
|
|
5557
|
+
const dir = join12(
|
|
5558
5558
|
input.rootDir,
|
|
5559
5559
|
"segment-frames",
|
|
5560
5560
|
`on-demand_${String(onDemandCalls++).padStart(5, "0")}`
|
|
@@ -5571,7 +5571,7 @@ function createSegmentVideoFrames(input) {
|
|
|
5571
5571
|
);
|
|
5572
5572
|
return {
|
|
5573
5573
|
async forFrames(frames, name) {
|
|
5574
|
-
const dir =
|
|
5574
|
+
const dir = join12(input.rootDir, "segment-frames", name);
|
|
5575
5575
|
const release = () => rmSync8(dir, { recursive: true, force: true });
|
|
5576
5576
|
try {
|
|
5577
5577
|
release();
|
|
@@ -5624,7 +5624,7 @@ function createSegmentVideoFrames(input) {
|
|
|
5624
5624
|
|
|
5625
5625
|
// src/services/render/videoFrameWindow.ts
|
|
5626
5626
|
import { readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
5627
|
-
import { join as
|
|
5627
|
+
import { join as join13 } from "node:path";
|
|
5628
5628
|
function shouldWindowVideoFrames(args) {
|
|
5629
5629
|
return args.segmented && args.env.HF_VIDEO_FRAME_WINDOW?.trim().toLowerCase() === "true";
|
|
5630
5630
|
}
|
|
@@ -5637,7 +5637,7 @@ function directoryBytes(path) {
|
|
|
5637
5637
|
return 0;
|
|
5638
5638
|
}
|
|
5639
5639
|
for (const entry of entries) {
|
|
5640
|
-
const child3 =
|
|
5640
|
+
const child3 = join13(path, entry.name);
|
|
5641
5641
|
try {
|
|
5642
5642
|
bytes += entry.isDirectory() ? directoryBytes(child3) : statSync4(child3).size;
|
|
5643
5643
|
} catch {
|
|
@@ -5655,7 +5655,7 @@ function trackSegmentFramePeak(frames, rootDir, onPeak) {
|
|
|
5655
5655
|
...worker,
|
|
5656
5656
|
async prepare(segment) {
|
|
5657
5657
|
await worker.prepare(segment);
|
|
5658
|
-
const held = directoryBytes(
|
|
5658
|
+
const held = directoryBytes(join13(rootDir, "segment-frames"));
|
|
5659
5659
|
if (held > peak) {
|
|
5660
5660
|
peak = held;
|
|
5661
5661
|
onPeak(peak);
|
|
@@ -5669,7 +5669,7 @@ function trackSegmentFramePeak(frames, rootDir, onPeak) {
|
|
|
5669
5669
|
// src/services/render/segmentManifest.ts
|
|
5670
5670
|
import { createHash as createHash2 } from "node:crypto";
|
|
5671
5671
|
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync4, statSync as statSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5672
|
-
import { join as
|
|
5672
|
+
import { join as join14 } from "node:path";
|
|
5673
5673
|
import { renameWithRetry as renameWithRetry2 } from "@hyperframes/engine";
|
|
5674
5674
|
var MANIFEST_FILENAME = "segments.json";
|
|
5675
5675
|
function computeSegmentPlanHash(input) {
|
|
@@ -5697,7 +5697,7 @@ function computeSegmentPlanHash(input) {
|
|
|
5697
5697
|
return createHash2("sha256").update(ordered.join(" ")).digest("hex").slice(0, 16);
|
|
5698
5698
|
}
|
|
5699
5699
|
function segmentDirFor(projectRendersDir, planHash) {
|
|
5700
|
-
return
|
|
5700
|
+
return join14(projectRendersDir, ".hf-segments", planHash);
|
|
5701
5701
|
}
|
|
5702
5702
|
function hasNumber(value, key) {
|
|
5703
5703
|
return key in value && typeof Reflect.get(value, key) === "number";
|
|
@@ -5715,7 +5715,7 @@ function isManifest(value) {
|
|
|
5715
5715
|
return Reflect.get(value, "version") === 1 && hasString(value, "planHash") && hasNumber(value, "totalFrames") && hasNumber(value, "segmentFrames") && Array.isArray(completed) && completed.every(isEntry);
|
|
5716
5716
|
}
|
|
5717
5717
|
function readSegmentManifest(segmentDir) {
|
|
5718
|
-
const path =
|
|
5718
|
+
const path = join14(segmentDir, MANIFEST_FILENAME);
|
|
5719
5719
|
if (!existsSync8(path)) return null;
|
|
5720
5720
|
try {
|
|
5721
5721
|
const parsed = JSON.parse(readFileSync4(path, "utf-8"));
|
|
@@ -5726,9 +5726,9 @@ function readSegmentManifest(segmentDir) {
|
|
|
5726
5726
|
}
|
|
5727
5727
|
function writeSegmentManifest(segmentDir, manifest) {
|
|
5728
5728
|
mkdirSync6(segmentDir, { recursive: true });
|
|
5729
|
-
const tmp =
|
|
5729
|
+
const tmp = join14(segmentDir, `${MANIFEST_FILENAME}.${process.pid}.tmp`);
|
|
5730
5730
|
writeFileSync3(tmp, JSON.stringify(manifest, null, 2), "utf-8");
|
|
5731
|
-
renameWithRetry2(tmp,
|
|
5731
|
+
renameWithRetry2(tmp, join14(segmentDir, MANIFEST_FILENAME));
|
|
5732
5732
|
}
|
|
5733
5733
|
async function probeSegmentFrameCount(path, probe) {
|
|
5734
5734
|
try {
|
|
@@ -6325,7 +6325,7 @@ function countAuthoredTimedClips(html) {
|
|
|
6325
6325
|
|
|
6326
6326
|
// src/services/render/stages/compileStage.ts
|
|
6327
6327
|
import { existsSync as existsSync12, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6328
|
-
import { join as
|
|
6328
|
+
import { join as join18 } from "node:path";
|
|
6329
6329
|
import { suggestMatchingPreset } from "@hyperframes/core";
|
|
6330
6330
|
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
6331
6331
|
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
@@ -6334,7 +6334,7 @@ import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolu
|
|
|
6334
6334
|
import { decodedUrlPath as decodedUrlPath2 } from "@hyperframes/parsers/asset-paths";
|
|
6335
6335
|
import { createHash as createHash6 } from "node:crypto";
|
|
6336
6336
|
import { createReadStream as createReadStream2, existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync7 } from "fs";
|
|
6337
|
-
import { join as
|
|
6337
|
+
import { join as join17, dirname as dirname8, resolve as resolve7, basename as basename5, relative as relative2, sep as sep2 } from "path";
|
|
6338
6338
|
import { parseHTML as parseHTML5 } from "linkedom";
|
|
6339
6339
|
import {
|
|
6340
6340
|
COMPOSITION_SOURCE_URL,
|
|
@@ -6581,7 +6581,7 @@ import {
|
|
|
6581
6581
|
// src/services/animatedGifPrep.ts
|
|
6582
6582
|
import { createHash as createHash4 } from "node:crypto";
|
|
6583
6583
|
import { copyFileSync as copyFileSync2, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync5, rmSync as rmSync9, statSync as statSync6 } from "node:fs";
|
|
6584
|
-
import { dirname as dirname6, isAbsolute as isAbsolute2, join as
|
|
6584
|
+
import { dirname as dirname6, isAbsolute as isAbsolute2, join as join15, resolve as resolve5 } from "node:path";
|
|
6585
6585
|
import { parseHTML as parseHTML3 } from "linkedom";
|
|
6586
6586
|
import { parseAnimatedGifMetadata } from "@hyperframes/core";
|
|
6587
6587
|
import { DEFAULT_VP9_CPU_USED, runFfmpeg as runFfmpeg2, renameWithRetry as renameWithRetry3 } from "@hyperframes/engine";
|
|
@@ -6824,7 +6824,7 @@ function replaceImageWithVideo(input) {
|
|
|
6824
6824
|
return video;
|
|
6825
6825
|
}
|
|
6826
6826
|
async function prepareAnimatedGifInputs(html, options) {
|
|
6827
|
-
const outputDir = options.outputDir ??
|
|
6827
|
+
const outputDir = options.outputDir ?? join15(options.downloadDir, PREPARED_GIF_SUBDIR);
|
|
6828
6828
|
const outputSrcPrefix = normalizeRelPath(options.outputSrcPrefix ?? PREPARED_GIF_SUBDIR);
|
|
6829
6829
|
const cacheDir = options.cacheDir ?? outputDir;
|
|
6830
6830
|
const { document: document2 } = parseHTML3(html);
|
|
@@ -6851,8 +6851,8 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6851
6851
|
);
|
|
6852
6852
|
const hash = computePreparedGifHash(bytes, loopIterations, padSeconds);
|
|
6853
6853
|
const filename = `${CACHE_SCHEMA}-${hash.slice(0, 24)}.webm`;
|
|
6854
|
-
const cachePath =
|
|
6855
|
-
const outputPath =
|
|
6854
|
+
const cachePath = join15(cacheDir, filename);
|
|
6855
|
+
const outputPath = join15(outputDir, filename);
|
|
6856
6856
|
const outputSrc = `${outputSrcPrefix}/${filename}`;
|
|
6857
6857
|
await ensurePreparedWebm({
|
|
6858
6858
|
sourcePath,
|
|
@@ -6897,7 +6897,7 @@ import {
|
|
|
6897
6897
|
statSync as statSync7,
|
|
6898
6898
|
writeFileSync as writeFileSync4
|
|
6899
6899
|
} from "node:fs";
|
|
6900
|
-
import { dirname as dirname7, join as
|
|
6900
|
+
import { dirname as dirname7, join as join16, resolve as resolve6 } from "node:path";
|
|
6901
6901
|
import { parseHTML as parseHTML4 } from "linkedom";
|
|
6902
6902
|
import { renameWithRetry as renameWithRetry4, runFfmpeg as runFfmpeg3, runFfprobe } from "@hyperframes/engine";
|
|
6903
6903
|
|
|
@@ -7171,7 +7171,7 @@ async function ensurePreparedJpeg(source, cachePath, outputPath, options) {
|
|
|
7171
7171
|
}
|
|
7172
7172
|
}
|
|
7173
7173
|
async function prepareHeifImageInputs(html, options) {
|
|
7174
|
-
const outputDir = options.outputDir ??
|
|
7174
|
+
const outputDir = options.outputDir ?? join16(options.downloadDir, PREPARED_HEIF_SUBDIR);
|
|
7175
7175
|
const outputSrcPrefix = normalizeRelPath(options.outputSrcPrefix ?? PREPARED_HEIF_SUBDIR);
|
|
7176
7176
|
const cacheDir = options.cacheDir ?? outputDir;
|
|
7177
7177
|
const preparedAssets = /* @__PURE__ */ new Map();
|
|
@@ -7192,7 +7192,7 @@ async function prepareHeifImageInputs(html, options) {
|
|
|
7192
7192
|
await Promise.all(
|
|
7193
7193
|
[...unique].map(
|
|
7194
7194
|
([filename, source]) => withMediaProbeSlot(
|
|
7195
|
-
() => ensurePreparedJpeg(source,
|
|
7195
|
+
() => ensurePreparedJpeg(source, join16(cacheDir, filename), join16(outputDir, filename), options)
|
|
7196
7196
|
).catch((error) => {
|
|
7197
7197
|
if (!options.onFailure) throw error;
|
|
7198
7198
|
failed.add(filename);
|
|
@@ -7204,8 +7204,8 @@ async function prepareHeifImageInputs(html, options) {
|
|
|
7204
7204
|
if (failed.has(filename)) continue;
|
|
7205
7205
|
const outputSrc = `${outputSrcPrefix}/${filename}`;
|
|
7206
7206
|
img.setAttribute("src", outputSrc);
|
|
7207
|
-
preparedAssets.set(outputSrc,
|
|
7208
|
-
preparedImages.push({ sourceSrc: src, outputSrc, outputPath:
|
|
7207
|
+
preparedAssets.set(outputSrc, join16(outputDir, filename));
|
|
7208
|
+
preparedImages.push({ sourceSrc: src, outputSrc, outputPath: join16(outputDir, filename) });
|
|
7209
7209
|
}
|
|
7210
7210
|
return { html: document2.toString(), preparedAssets, preparedImages };
|
|
7211
7211
|
}
|
|
@@ -7541,7 +7541,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
7541
7541
|
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath, probes) {
|
|
7542
7542
|
let filePath = src;
|
|
7543
7543
|
if (isHttpUrl(src)) {
|
|
7544
|
-
const remoteDir =
|
|
7544
|
+
const remoteDir = join17(downloadDir, REMOTE_MEDIA_SUBDIR);
|
|
7545
7545
|
if (!existsSync11(remoteDir)) mkdirSync9(remoteDir, { recursive: true });
|
|
7546
7546
|
try {
|
|
7547
7547
|
filePath = await downloadToTemp(src, remoteDir, void 0, void 0, void 0, {
|
|
@@ -7552,7 +7552,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
7552
7552
|
}
|
|
7553
7553
|
} else {
|
|
7554
7554
|
filePath = decodedUrlPath2(src);
|
|
7555
|
-
if (!filePath.startsWith("/")) filePath =
|
|
7555
|
+
if (!filePath.startsWith("/")) filePath = join17(baseDir, filePath);
|
|
7556
7556
|
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
7557
7557
|
}
|
|
7558
7558
|
if (!isHttpUrl(filePath) && !existsSync11(filePath)) {
|
|
@@ -8168,7 +8168,7 @@ async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLab
|
|
|
8168
8168
|
const remoteMediaAssets = /* @__PURE__ */ new Map();
|
|
8169
8169
|
const urlToRelPath = /* @__PURE__ */ new Map();
|
|
8170
8170
|
for (const [url, absPath] of urlToLocal) {
|
|
8171
|
-
const relPath = `${REMOTE_MEDIA_SUBDIR}/${
|
|
8171
|
+
const relPath = `${REMOTE_MEDIA_SUBDIR}/${basename5(absPath)}`;
|
|
8172
8172
|
remoteMediaAssets.set(relPath, absPath);
|
|
8173
8173
|
urlToRelPath.set(url, relPath);
|
|
8174
8174
|
}
|
|
@@ -8192,7 +8192,7 @@ async function localizeRemoteMediaSources(html, downloadDir) {
|
|
|
8192
8192
|
return downloadAndRewriteUrls(
|
|
8193
8193
|
urlSet,
|
|
8194
8194
|
html,
|
|
8195
|
-
|
|
8195
|
+
join17(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
8196
8196
|
"Remote media download failed",
|
|
8197
8197
|
"Localized remote media source(s)"
|
|
8198
8198
|
);
|
|
@@ -8207,7 +8207,7 @@ async function localizeRemoteImageSources(html, downloadDir) {
|
|
|
8207
8207
|
return downloadAndRewriteUrls(
|
|
8208
8208
|
urlSet,
|
|
8209
8209
|
html,
|
|
8210
|
-
|
|
8210
|
+
join17(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
8211
8211
|
"Remote image download failed",
|
|
8212
8212
|
"Localized remote image source(s)"
|
|
8213
8213
|
);
|
|
@@ -8223,7 +8223,7 @@ async function localizeRemoteBackgroundImages(html, downloadDir) {
|
|
|
8223
8223
|
return downloadAndRewriteUrls(
|
|
8224
8224
|
urlSet,
|
|
8225
8225
|
html,
|
|
8226
|
-
|
|
8226
|
+
join17(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
8227
8227
|
"Remote background-image download failed",
|
|
8228
8228
|
"Localized remote background-image(s)",
|
|
8229
8229
|
// Quoted url('..')/url("..") are rewritten by downloadAndRewriteUrls' default
|
|
@@ -8378,7 +8378,7 @@ function localizeFontFaceUrls(html, downloadDir) {
|
|
|
8378
8378
|
return downloadAndRewriteUrls(
|
|
8379
8379
|
collectFontFaceUrls(html),
|
|
8380
8380
|
html,
|
|
8381
|
-
|
|
8381
|
+
join17(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
8382
8382
|
"Remote font download failed",
|
|
8383
8383
|
"Localized remote font face(s)",
|
|
8384
8384
|
(h, url, relPath) => h.replaceAll(`url(${url})`, `url("${relPath}")`)
|
|
@@ -9055,10 +9055,10 @@ function adaptAspectAgnosticResolution(requested, aspectAgnostic, width, height,
|
|
|
9055
9055
|
}
|
|
9056
9056
|
function resolveEntryHtmlPath(input) {
|
|
9057
9057
|
const { projectDir, workDir, entryFile, log } = input;
|
|
9058
|
-
const htmlPath =
|
|
9058
|
+
const htmlPath = join18(projectDir, entryFile);
|
|
9059
9059
|
const rawEntry = readTextFile2(htmlPath);
|
|
9060
9060
|
if (entryFile === "index.html" || !isTemplateEntry(rawEntry)) return htmlPath;
|
|
9061
|
-
const projectIndexPath =
|
|
9061
|
+
const projectIndexPath = join18(projectDir, "index.html");
|
|
9062
9062
|
if (!existsSync12(projectIndexPath)) {
|
|
9063
9063
|
throw new Error(
|
|
9064
9064
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
@@ -9074,7 +9074,7 @@ function resolveEntryHtmlPath(input) {
|
|
|
9074
9074
|
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
9075
9075
|
);
|
|
9076
9076
|
}
|
|
9077
|
-
const wrapperPath =
|
|
9077
|
+
const wrapperPath = join18(workDir, "standalone-entry.html");
|
|
9078
9078
|
writeFileSync5(wrapperPath, standaloneHtml, "utf-8");
|
|
9079
9079
|
log.info("Extracted standalone entry from index.html host context", { entryFile });
|
|
9080
9080
|
return wrapperPath;
|
|
@@ -9095,7 +9095,7 @@ async function runCompileStage(input) {
|
|
|
9095
9095
|
} = input;
|
|
9096
9096
|
const htmlPath = resolveEntryHtmlPath({ projectDir, workDir, entryFile, log });
|
|
9097
9097
|
const compileStart = Date.now();
|
|
9098
|
-
const compiled = await compileForRender(projectDir, htmlPath,
|
|
9098
|
+
const compiled = await compileForRender(projectDir, htmlPath, join18(workDir, "downloads"), {
|
|
9099
9099
|
log,
|
|
9100
9100
|
failClosedFontFetch: failClosedFontFetch === true,
|
|
9101
9101
|
allowSystemFontCapture,
|
|
@@ -9103,7 +9103,7 @@ async function runCompileStage(input) {
|
|
|
9103
9103
|
variables: input.variables,
|
|
9104
9104
|
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
9105
9105
|
pageMotionBlur: input.pageMotionBlur,
|
|
9106
|
-
animatedGifCacheDir: cfg.extractCacheDir ?
|
|
9106
|
+
animatedGifCacheDir: cfg.extractCacheDir ? join18(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
9107
9107
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
9108
9108
|
});
|
|
9109
9109
|
assertNotAborted();
|
|
@@ -9160,7 +9160,7 @@ async function runCompileStage(input) {
|
|
|
9160
9160
|
};
|
|
9161
9161
|
await preflightCompositionAssetMediaTypes({
|
|
9162
9162
|
projectDir,
|
|
9163
|
-
compiledDir:
|
|
9163
|
+
compiledDir: join18(workDir, "compiled"),
|
|
9164
9164
|
composition,
|
|
9165
9165
|
signal: abortSignal
|
|
9166
9166
|
});
|
|
@@ -9206,7 +9206,7 @@ async function runCompileStage(input) {
|
|
|
9206
9206
|
|
|
9207
9207
|
// src/services/render/stages/probeStage.ts
|
|
9208
9208
|
import { createHash as createHash7 } from "node:crypto";
|
|
9209
|
-
import { basename as
|
|
9209
|
+
import { basename as basename6, join as join19 } from "node:path";
|
|
9210
9210
|
import { parseHTML as parseHTML6 } from "linkedom";
|
|
9211
9211
|
import {
|
|
9212
9212
|
closeCaptureSession as closeCaptureSession4,
|
|
@@ -9356,7 +9356,7 @@ function probeRequiresBrowser(args) {
|
|
|
9356
9356
|
function reportedLengthSource(source) {
|
|
9357
9357
|
if (!source) return null;
|
|
9358
9358
|
if (source.from !== "media") return { from: source.from };
|
|
9359
|
-
const name =
|
|
9359
|
+
const name = basename6(source.src.split(/[?#]/, 1)[0] ?? "");
|
|
9360
9360
|
const srcHash = createHash7("sha256").update(name).digest("hex").slice(0, 12);
|
|
9361
9361
|
return { from: "media", tag: source.tag, trimmed: source.trimmed, srcHash };
|
|
9362
9362
|
}
|
|
@@ -9415,7 +9415,7 @@ async function runProbeStage(input) {
|
|
|
9415
9415
|
});
|
|
9416
9416
|
fileServer = await createFileServer({
|
|
9417
9417
|
projectDir,
|
|
9418
|
-
compiledDir:
|
|
9418
|
+
compiledDir: join19(workDir, "compiled"),
|
|
9419
9419
|
port: 0,
|
|
9420
9420
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
9421
9421
|
fps: job.config.fps
|
|
@@ -9445,7 +9445,7 @@ async function runProbeStage(input) {
|
|
|
9445
9445
|
log.info("Creating capture session...", { attempt, maxAttempts: PROBE_MAX_ATTEMPTS });
|
|
9446
9446
|
probeSession = await createCaptureSession4(
|
|
9447
9447
|
fileServer.url,
|
|
9448
|
-
|
|
9448
|
+
join19(workDir, "probe"),
|
|
9449
9449
|
captureOpts,
|
|
9450
9450
|
null,
|
|
9451
9451
|
probeCfg
|
|
@@ -9530,7 +9530,7 @@ async function runProbeStage(input) {
|
|
|
9530
9530
|
});
|
|
9531
9531
|
probeSession = await createCaptureSession4(
|
|
9532
9532
|
fileServer.url,
|
|
9533
|
-
|
|
9533
|
+
join19(workDir, "probe-screenshot"),
|
|
9534
9534
|
captureOpts,
|
|
9535
9535
|
null,
|
|
9536
9536
|
{ ...probeCfg, forceScreenshot: true }
|
|
@@ -9568,7 +9568,7 @@ async function runProbeStage(input) {
|
|
|
9568
9568
|
compiled,
|
|
9569
9569
|
resolutions,
|
|
9570
9570
|
projectDir,
|
|
9571
|
-
|
|
9571
|
+
join19(workDir, "downloads")
|
|
9572
9572
|
);
|
|
9573
9573
|
assertNotAborted();
|
|
9574
9574
|
composition.videos = compiled.videos;
|
|
@@ -9759,7 +9759,7 @@ async function runProbeStage(input) {
|
|
|
9759
9759
|
}
|
|
9760
9760
|
await preflightCompositionAssetMediaTypes({
|
|
9761
9761
|
projectDir,
|
|
9762
|
-
compiledDir:
|
|
9762
|
+
compiledDir: join19(workDir, "compiled"),
|
|
9763
9763
|
composition,
|
|
9764
9764
|
signal: abortSignal
|
|
9765
9765
|
});
|
|
@@ -9926,7 +9926,7 @@ function validateDistributedDuration(input) {
|
|
|
9926
9926
|
|
|
9927
9927
|
// src/services/render/stages/extractVideosStage.ts
|
|
9928
9928
|
import { existsSync as existsSync13 } from "node:fs";
|
|
9929
|
-
import { join as
|
|
9929
|
+
import { join as join20 } from "node:path";
|
|
9930
9930
|
import {
|
|
9931
9931
|
classifyVideoExtractionError,
|
|
9932
9932
|
createFrameLookupTable as createFrameLookupTable3,
|
|
@@ -10272,14 +10272,14 @@ async function runExtractVideosStage(input) {
|
|
|
10272
10272
|
// because short boundary counts can differ by one frame.
|
|
10273
10273
|
{
|
|
10274
10274
|
fps: job.config.fps,
|
|
10275
|
-
outputDir:
|
|
10275
|
+
outputDir: join20(compiledDir, "__hyperframes_video_frames"),
|
|
10276
10276
|
format: job.config.videoFrameFormat ?? "auto",
|
|
10277
10277
|
toneMapHdrToSdr: rendersSdrOnly(job.config, composition),
|
|
10278
10278
|
timelineEnd: composition.duration,
|
|
10279
10279
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
10280
10280
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
10281
10281
|
deferRangeExtraction,
|
|
10282
|
-
contentKeyedDirs: [
|
|
10282
|
+
contentKeyedDirs: [join20(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
10283
10283
|
urlByAbsentLocalPath,
|
|
10284
10284
|
onProgress: ({ planned = 0, done = 0 }) => {
|
|
10285
10285
|
framesPlanned += planned;
|
|
@@ -10462,7 +10462,7 @@ async function planVideoLayerPassthrough(input) {
|
|
|
10462
10462
|
|
|
10463
10463
|
// src/services/render/stages/audioStage.ts
|
|
10464
10464
|
import { existsSync as existsSync16 } from "node:fs";
|
|
10465
|
-
import { join as
|
|
10465
|
+
import { join as join23 } from "node:path";
|
|
10466
10466
|
import {
|
|
10467
10467
|
MIXED_AUDIO_FILENAME,
|
|
10468
10468
|
audioFailureDiagnostics,
|
|
@@ -10473,7 +10473,7 @@ import {
|
|
|
10473
10473
|
|
|
10474
10474
|
// src/services/render/audioDeliveryLimit.ts
|
|
10475
10475
|
import { existsSync as existsSync15 } from "node:fs";
|
|
10476
|
-
import { dirname as dirname10, join as
|
|
10476
|
+
import { dirname as dirname10, join as join22 } from "node:path";
|
|
10477
10477
|
import {
|
|
10478
10478
|
applyAudioFxChain,
|
|
10479
10479
|
describeFfmpegFailure as describeFfmpegFailure3,
|
|
@@ -10483,8 +10483,7 @@ import {
|
|
|
10483
10483
|
|
|
10484
10484
|
// src/services/render/audioPadTrim.ts
|
|
10485
10485
|
import { spawn } from "node:child_process";
|
|
10486
|
-
import {
|
|
10487
|
-
import { dirname as dirname9, join as join22 } from "node:path";
|
|
10486
|
+
import { dirname as dirname9, join as join21 } from "node:path";
|
|
10488
10487
|
import {
|
|
10489
10488
|
DEFAULT_CONFIG,
|
|
10490
10489
|
describeFfmpegFailure as describeFfmpegFailure2,
|
|
@@ -10591,6 +10590,7 @@ async function measureTruePeakDbfs(audioPath, options) {
|
|
|
10591
10590
|
}
|
|
10592
10591
|
|
|
10593
10592
|
// src/services/render/audioPadTrim.ts
|
|
10593
|
+
import { createStagingDir as createStagingDir3 } from "@hyperframes/core/atomic-file";
|
|
10594
10594
|
var AUDIO_DURATION_TOLERANCE_SECONDS = 1e-3;
|
|
10595
10595
|
var AAC_DELIVERY_TRUE_PEAK_DBFS = -1;
|
|
10596
10596
|
var AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB = 0.5;
|
|
@@ -10763,7 +10763,7 @@ async function constrainAacTruePeak(input) {
|
|
|
10763
10763
|
}
|
|
10764
10764
|
async function cutSource(input, scratchDir, attenuationDb) {
|
|
10765
10765
|
if (!input.sourcePath || !input.rewriteSource) return true;
|
|
10766
|
-
const cutPath =
|
|
10766
|
+
const cutPath = join21(scratchDir, "source.wav");
|
|
10767
10767
|
try {
|
|
10768
10768
|
const result = await input.rewriteSource(input.sourcePath, cutPath, gainFilter(attenuationDb));
|
|
10769
10769
|
if (!result.success) throw new Error(result.error);
|
|
@@ -10777,8 +10777,8 @@ async function cutSource(input, scratchDir, attenuationDb) {
|
|
|
10777
10777
|
async function enforceAacTruePeak(input) {
|
|
10778
10778
|
let scratchDir;
|
|
10779
10779
|
try {
|
|
10780
|
-
scratchDir =
|
|
10781
|
-
const correctedPath =
|
|
10780
|
+
scratchDir = createStagingDir3(dirname9(input.audioPath), ".true-peak-");
|
|
10781
|
+
const correctedPath = join21(scratchDir, "audio.m4a");
|
|
10782
10782
|
const measurements = [];
|
|
10783
10783
|
let attenuationDb = 0;
|
|
10784
10784
|
let sourcePeakDbfs;
|
|
@@ -10988,7 +10988,7 @@ async function limitDeliveryPeaks(input, deps = {
|
|
|
10988
10988
|
const peak = await deps.measure(audioPath, { timeoutMs, signal });
|
|
10989
10989
|
if (peak <= AAC_DELIVERY_TRUE_PEAK_DBFS) return { success: true };
|
|
10990
10990
|
const alternating = ["a", "b"].map(
|
|
10991
|
-
(k) =>
|
|
10991
|
+
(k) => join22(dirname10(sourcePath), `audio-source-limited-${k}.wav`)
|
|
10992
10992
|
);
|
|
10993
10993
|
let encodedFrom;
|
|
10994
10994
|
let ceiling = AAC_DELIVERY_TRUE_PEAK_DBFS;
|
|
@@ -11060,7 +11060,7 @@ function runAudioStage(input) {
|
|
|
11060
11060
|
}
|
|
11061
11061
|
async function runAudioStageScoped(input) {
|
|
11062
11062
|
const stageStart = Date.now();
|
|
11063
|
-
const pcmSourcePath =
|
|
11063
|
+
const pcmSourcePath = join23(input.workDir, "audio-source.wav");
|
|
11064
11064
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
11065
11065
|
let deliverySourcePath;
|
|
11066
11066
|
try {
|
|
@@ -11125,7 +11125,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
11125
11125
|
assertNotAborted,
|
|
11126
11126
|
log
|
|
11127
11127
|
} = input;
|
|
11128
|
-
const audioOutputPath =
|
|
11128
|
+
const audioOutputPath = join23(workDir, MIXED_AUDIO_FILENAME);
|
|
11129
11129
|
let hasAudio = false;
|
|
11130
11130
|
let audioError;
|
|
11131
11131
|
let audioFailures;
|
|
@@ -11136,7 +11136,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
11136
11136
|
audioResult = await processCompositionAudio(
|
|
11137
11137
|
audios,
|
|
11138
11138
|
projectDir,
|
|
11139
|
-
|
|
11139
|
+
join23(workDir, "audio-work"),
|
|
11140
11140
|
audioOutputPath,
|
|
11141
11141
|
duration,
|
|
11142
11142
|
abortSignal,
|
|
@@ -11205,7 +11205,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
11205
11205
|
|
|
11206
11206
|
// src/services/render/stages/captureStage.ts
|
|
11207
11207
|
import { existsSync as existsSync17, readdirSync as readdirSync4, statfsSync as statfsSync2 } from "node:fs";
|
|
11208
|
-
import { join as
|
|
11208
|
+
import { join as join24 } from "node:path";
|
|
11209
11209
|
import {
|
|
11210
11210
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
11211
11211
|
frameFileExtension,
|
|
@@ -11278,13 +11278,13 @@ function measureCaptureFrameBytes(workDir, framesDir) {
|
|
|
11278
11278
|
if (!existsSync17(workDir)) return total;
|
|
11279
11279
|
for (const attempt of readdirSync4(workDir)) {
|
|
11280
11280
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
11281
|
-
total += sampleDirectoryBytes(
|
|
11281
|
+
total += sampleDirectoryBytes(join24(workDir, attempt));
|
|
11282
11282
|
continue;
|
|
11283
11283
|
}
|
|
11284
11284
|
if (!/^capture-attempt-\d+$/.test(attempt)) continue;
|
|
11285
|
-
const attemptDir =
|
|
11285
|
+
const attemptDir = join24(workDir, attempt);
|
|
11286
11286
|
for (const name of readdirSync4(attemptDir)) {
|
|
11287
|
-
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(
|
|
11287
|
+
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join24(attemptDir, name));
|
|
11288
11288
|
}
|
|
11289
11289
|
}
|
|
11290
11290
|
return total;
|
|
@@ -11583,7 +11583,7 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
11583
11583
|
|
|
11584
11584
|
// src/services/render/stages/captureHdrStage.ts
|
|
11585
11585
|
import { existsSync as existsSync19, mkdirSync as mkdirSync10 } from "node:fs";
|
|
11586
|
-
import { join as
|
|
11586
|
+
import { join as join28 } from "node:path";
|
|
11587
11587
|
import {
|
|
11588
11588
|
closeCaptureSession as closeCaptureSession7,
|
|
11589
11589
|
cloneCaptureWarnings as cloneCaptureWarnings2,
|
|
@@ -11655,7 +11655,7 @@ function createHdrImageTransferCache(options = {}) {
|
|
|
11655
11655
|
}
|
|
11656
11656
|
|
|
11657
11657
|
// src/services/render/stages/captureHdrSequentialLoop.ts
|
|
11658
|
-
import { join as
|
|
11658
|
+
import { join as join25 } from "node:path";
|
|
11659
11659
|
import {
|
|
11660
11660
|
TRANSITIONS,
|
|
11661
11661
|
crossfade
|
|
@@ -11780,7 +11780,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
11780
11780
|
);
|
|
11781
11781
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
11782
11782
|
writeFileExclusiveSync(
|
|
11783
|
-
|
|
11783
|
+
join25(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
11784
11784
|
normalCanvas
|
|
11785
11785
|
);
|
|
11786
11786
|
}
|
|
@@ -11812,7 +11812,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
11812
11812
|
}
|
|
11813
11813
|
|
|
11814
11814
|
// src/services/render/stages/captureHdrHybridLoop.ts
|
|
11815
|
-
import { join as
|
|
11815
|
+
import { join as join27 } from "node:path";
|
|
11816
11816
|
import {
|
|
11817
11817
|
TRANSITIONS as TRANSITIONS2,
|
|
11818
11818
|
closeCaptureSession as closeCaptureSession6,
|
|
@@ -11826,7 +11826,7 @@ import {
|
|
|
11826
11826
|
// src/services/shaderTransitionWorkerPool.ts
|
|
11827
11827
|
import { Worker } from "node:worker_threads";
|
|
11828
11828
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
11829
|
-
import { dirname as dirname11, join as
|
|
11829
|
+
import { dirname as dirname11, join as join26 } from "node:path";
|
|
11830
11830
|
import { createRequire } from "node:module";
|
|
11831
11831
|
import { existsSync as existsSync18 } from "node:fs";
|
|
11832
11832
|
import { cpus as cpus2 } from "node:os";
|
|
@@ -11840,9 +11840,9 @@ function resolveWorkerEntry(explicit) {
|
|
|
11840
11840
|
return { path: override, isTs };
|
|
11841
11841
|
}
|
|
11842
11842
|
const moduleDir = dirname11(fileURLToPath3(import.meta.url));
|
|
11843
|
-
const jsPath =
|
|
11843
|
+
const jsPath = join26(moduleDir, "shaderTransitionWorker.js");
|
|
11844
11844
|
if (existsSync18(jsPath)) return { path: jsPath, isTs: false };
|
|
11845
|
-
const tsPath =
|
|
11845
|
+
const tsPath = join26(moduleDir, "shaderTransitionWorker.ts");
|
|
11846
11846
|
return { path: tsPath, isTs: true };
|
|
11847
11847
|
}
|
|
11848
11848
|
function buildExecArgv(entryIsTs) {
|
|
@@ -12210,7 +12210,7 @@ async function runHybridLayeredFrameLoop(input) {
|
|
|
12210
12210
|
);
|
|
12211
12211
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
12212
12212
|
writeFileExclusiveSync(
|
|
12213
|
-
|
|
12213
|
+
join27(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
12214
12214
|
canvas
|
|
12215
12215
|
);
|
|
12216
12216
|
}
|
|
@@ -12401,7 +12401,7 @@ async function runCaptureHdrStage(input) {
|
|
|
12401
12401
|
if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
|
|
12402
12402
|
}
|
|
12403
12403
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
12404
|
-
const debugDumpDir = debugDumpEnabled ?
|
|
12404
|
+
const debugDumpDir = debugDumpEnabled ? join28(framesDir, "debug-composite") : null;
|
|
12405
12405
|
if (debugDumpDir && !existsSync19(debugDumpDir)) {
|
|
12406
12406
|
mkdirSync10(debugDumpDir, { recursive: true });
|
|
12407
12407
|
}
|
|
@@ -12568,7 +12568,7 @@ import {
|
|
|
12568
12568
|
writeFileSync as writeFileSync6
|
|
12569
12569
|
} from "node:fs";
|
|
12570
12570
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
12571
|
-
import { dirname as dirname12, join as
|
|
12571
|
+
import { dirname as dirname12, join as join29 } from "node:path";
|
|
12572
12572
|
import {
|
|
12573
12573
|
encodeFramesChunkedConcat,
|
|
12574
12574
|
encodeFramesFromDir,
|
|
@@ -12779,7 +12779,7 @@ function encodeWhileCapturing(input, totalFrames) {
|
|
|
12779
12779
|
await new Promise((resolve13) => wake = resolve13);
|
|
12780
12780
|
}
|
|
12781
12781
|
if (signal.aborted) return;
|
|
12782
|
-
const mergedPath =
|
|
12782
|
+
const mergedPath = join29(input.framesDir, formatCaptureFrameName(next, "jpg"));
|
|
12783
12783
|
const frame = await readLandedFrame(landed.get(next), mergedPath);
|
|
12784
12784
|
landed.delete(next);
|
|
12785
12785
|
if (!await stream.writeFrame(frame)) {
|
|
@@ -12862,11 +12862,11 @@ async function runEncodeStage(input) {
|
|
|
12862
12862
|
);
|
|
12863
12863
|
}
|
|
12864
12864
|
captured.forEach((name, i) => {
|
|
12865
|
-
const dst =
|
|
12866
|
-
copyFileSync4(
|
|
12865
|
+
const dst = join29(outputPath, formatExportFrameName(i, "png"));
|
|
12866
|
+
copyFileSync4(join29(framesDir, name), dst);
|
|
12867
12867
|
});
|
|
12868
12868
|
if (hasAudio && audioOutputPath && existsSync20(audioOutputPath)) {
|
|
12869
|
-
copyFileSync4(audioOutputPath,
|
|
12869
|
+
copyFileSync4(audioOutputPath, join29(outputPath, MIXED_AUDIO_FILENAME2));
|
|
12870
12870
|
log.info(
|
|
12871
12871
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
12872
12872
|
);
|
|
@@ -12886,7 +12886,7 @@ async function runEncodeStage(input) {
|
|
|
12886
12886
|
const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
|
|
12887
12887
|
fps: job.config.fps,
|
|
12888
12888
|
loop,
|
|
12889
|
-
palettePath:
|
|
12889
|
+
palettePath: join29(dirname12(videoOnlyPath), "gif-palette.png"),
|
|
12890
12890
|
preserveAlpha: needsAlpha,
|
|
12891
12891
|
signal: abortSignal,
|
|
12892
12892
|
timeout: engineCfg.ffmpegEncodeTimeout
|
|
@@ -13097,7 +13097,7 @@ function sampleDirectoryBytes(dir) {
|
|
|
13097
13097
|
continue;
|
|
13098
13098
|
}
|
|
13099
13099
|
for (const name of entries) {
|
|
13100
|
-
const full =
|
|
13100
|
+
const full = join30(current, name);
|
|
13101
13101
|
try {
|
|
13102
13102
|
const st = statSync9(full);
|
|
13103
13103
|
if (st.isDirectory()) {
|
|
@@ -13253,7 +13253,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
13253
13253
|
const ranges = [];
|
|
13254
13254
|
let rangeStart = null;
|
|
13255
13255
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
13256
|
-
const framePath =
|
|
13256
|
+
const framePath = join30(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
13257
13257
|
const missing = !existsSync21(framePath) || statSync9(framePath).size <= 8;
|
|
13258
13258
|
if (missing && rangeStart === null) {
|
|
13259
13259
|
rangeStart = frameIndex;
|
|
@@ -13270,7 +13270,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
13270
13270
|
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
|
|
13271
13271
|
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
13272
13272
|
const batches = [];
|
|
13273
|
-
const outputDirFor = (batchIndex, workerId) =>
|
|
13273
|
+
const outputDirFor = (batchIndex, workerId) => join30(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
|
|
13274
13274
|
if (stride > 1) {
|
|
13275
13275
|
const spans = /* @__PURE__ */ new Map();
|
|
13276
13276
|
for (const range of ranges) {
|
|
@@ -13330,8 +13330,8 @@ function getNextRetryWorkerCount(currentWorkers) {
|
|
|
13330
13330
|
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
13331
13331
|
}
|
|
13332
13332
|
function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
|
|
13333
|
-
if (platform2 === "win32" || platform2 === "darwin") return
|
|
13334
|
-
return
|
|
13333
|
+
if (platform2 === "win32" || platform2 === "darwin") return join30(systemTempDir, "hf-render-");
|
|
13334
|
+
return join30(dirname13(outputPath), `work-${jobId}-`);
|
|
13335
13335
|
}
|
|
13336
13336
|
var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
|
|
13337
13337
|
function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
|
|
@@ -13378,7 +13378,7 @@ The composition is too large for the available memory. To reduce memory pressure
|
|
|
13378
13378
|
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
13379
13379
|
let captured = 0;
|
|
13380
13380
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
13381
|
-
const framePath =
|
|
13381
|
+
const framePath = join30(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
13382
13382
|
if (existsSync21(framePath)) captured++;
|
|
13383
13383
|
}
|
|
13384
13384
|
return captured;
|
|
@@ -13404,7 +13404,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
13404
13404
|
reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
|
|
13405
13405
|
});
|
|
13406
13406
|
pendingTransientRetry = false;
|
|
13407
|
-
const attemptWorkDir =
|
|
13407
|
+
const attemptWorkDir = join30(options.workDir, `capture-attempt-${attempt}`);
|
|
13408
13408
|
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
13409
13409
|
missingRanges,
|
|
13410
13410
|
currentWorkers,
|
|
@@ -14037,7 +14037,7 @@ function deVerifyFallbackTelemetry(err) {
|
|
|
14037
14037
|
}
|
|
14038
14038
|
function resolveRenderDebugDir() {
|
|
14039
14039
|
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname13(fileURLToPath4(import.meta.url)), "../..");
|
|
14040
|
-
return
|
|
14040
|
+
return join30(producerRoot, ".debug");
|
|
14041
14041
|
}
|
|
14042
14042
|
async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
|
|
14043
14043
|
validateHlsRenderConfig(job.config);
|
|
@@ -14045,10 +14045,10 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
14045
14045
|
const debugDir = resolveRenderDebugDir();
|
|
14046
14046
|
const outputDir = dirname13(outputPath);
|
|
14047
14047
|
if (!existsSync21(outputDir)) mkdirSync12(outputDir, { recursive: true });
|
|
14048
|
-
const workDir = job.config.debug ?
|
|
14048
|
+
const workDir = job.config.debug ? join30(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
14049
14049
|
const pipelineStart = Date.now();
|
|
14050
14050
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
14051
|
-
const logPath = job.config.debug ?
|
|
14051
|
+
const logPath = job.config.debug ? join30(workDir, "render.log") : null;
|
|
14052
14052
|
const execution = new RenderExecutionContext({
|
|
14053
14053
|
request: { renderJobId: job.id, projectDir, outputPath },
|
|
14054
14054
|
logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
|
|
@@ -14110,7 +14110,7 @@ async function executeRenderPipeline(input) {
|
|
|
14110
14110
|
imageDecodeFailures: 0
|
|
14111
14111
|
};
|
|
14112
14112
|
let hdrPerf;
|
|
14113
|
-
const perfOutputPath =
|
|
14113
|
+
const perfOutputPath = join30(workDir, "perf-summary.json");
|
|
14114
14114
|
const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
|
|
14115
14115
|
const observability = new RenderObservabilityRecorder({
|
|
14116
14116
|
pipelineStartMs: pipelineStart,
|
|
@@ -14215,7 +14215,7 @@ async function executeRenderPipeline(input) {
|
|
|
14215
14215
|
requestedWorkers: job.config.workers ?? "auto"
|
|
14216
14216
|
});
|
|
14217
14217
|
const entryFile = job.config.entryFile || "index.html";
|
|
14218
|
-
const htmlPath =
|
|
14218
|
+
const htmlPath = join30(projectDir, entryFile);
|
|
14219
14219
|
if (!existsSync21(htmlPath)) {
|
|
14220
14220
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
14221
14221
|
}
|
|
@@ -14392,7 +14392,7 @@ async function executeRenderPipeline(input) {
|
|
|
14392
14392
|
canvas: { width, height },
|
|
14393
14393
|
page: probeSession.page,
|
|
14394
14394
|
projectDir,
|
|
14395
|
-
compiledDir:
|
|
14395
|
+
compiledDir: join30(workDir, "compiled")
|
|
14396
14396
|
});
|
|
14397
14397
|
if (videoLayerPlan.videoId === void 0) {
|
|
14398
14398
|
log.info(`[Render] Video layer passthrough off: ${videoLayerPlan.reason}`);
|
|
@@ -14410,7 +14410,7 @@ async function executeRenderPipeline(input) {
|
|
|
14410
14410
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
|
|
14411
14411
|
code: "extract_video"
|
|
14412
14412
|
});
|
|
14413
|
-
const compiledDir =
|
|
14413
|
+
const compiledDir = join30(workDir, "compiled");
|
|
14414
14414
|
const segmentsCapture = (layeredOrEffectRoute) => shouldSegmentCapture({
|
|
14415
14415
|
env: process.env,
|
|
14416
14416
|
durationSeconds: probeResult.duration,
|
|
@@ -14541,7 +14541,7 @@ async function executeRenderPipeline(input) {
|
|
|
14541
14541
|
try {
|
|
14542
14542
|
fileServer = await createFileServer({
|
|
14543
14543
|
projectDir,
|
|
14544
|
-
compiledDir:
|
|
14544
|
+
compiledDir: join30(workDir, "compiled"),
|
|
14545
14545
|
port: 0,
|
|
14546
14546
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
14547
14547
|
fps: job.config.fps
|
|
@@ -14559,7 +14559,7 @@ async function executeRenderPipeline(input) {
|
|
|
14559
14559
|
if (!activeFileServer) {
|
|
14560
14560
|
throw new Error("File server failed to initialize before frame capture");
|
|
14561
14561
|
}
|
|
14562
|
-
const framesDir =
|
|
14562
|
+
const framesDir = join30(workDir, "captured-frames");
|
|
14563
14563
|
if (!existsSync21(framesDir)) mkdirSync12(framesDir, { recursive: true });
|
|
14564
14564
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
14565
14565
|
code: "start_browsers"
|
|
@@ -15123,7 +15123,7 @@ async function executeRenderPipeline(input) {
|
|
|
15123
15123
|
hls: ".mp4"
|
|
15124
15124
|
};
|
|
15125
15125
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
15126
|
-
const videoOnlyPath =
|
|
15126
|
+
const videoOnlyPath = join30(workDir, `video-only${videoExt}`);
|
|
15127
15127
|
const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
|
|
15128
15128
|
if (usePageSideCompositingForTransitions) {
|
|
15129
15129
|
activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
|
|
@@ -15409,7 +15409,7 @@ async function executeRenderPipeline(input) {
|
|
|
15409
15409
|
pageMotionBlur: job.config.pageMotionBlur,
|
|
15410
15410
|
captureQuality: captureOptions.quality
|
|
15411
15411
|
});
|
|
15412
|
-
const segmentDir = segmentDirFor(
|
|
15412
|
+
const segmentDir = segmentDirFor(join30(projectDir, "renders"), segmentPlanHash);
|
|
15413
15413
|
let completedSegments = /* @__PURE__ */ new Set();
|
|
15414
15414
|
if (job.config.resumeSegments === true) {
|
|
15415
15415
|
const existing = readSegmentManifest(segmentDir);
|
|
@@ -16032,7 +16032,7 @@ async function executeRenderPipeline(input) {
|
|
|
16032
16032
|
}
|
|
16033
16033
|
if (job.config.debug) {
|
|
16034
16034
|
if (!isPngSequence && !isHls && existsSync21(stagedOutputPath)) {
|
|
16035
|
-
const debugOutput =
|
|
16035
|
+
const debugOutput = join30(workDir, `output${videoExt}`);
|
|
16036
16036
|
copyFileSync5(stagedOutputPath, debugOutput);
|
|
16037
16037
|
}
|
|
16038
16038
|
}
|
|
@@ -16146,16 +16146,17 @@ import {
|
|
|
16146
16146
|
REFERENCE_BACKGROUND_FIELD as REFERENCE_BACKGROUND_FIELD4,
|
|
16147
16147
|
referenceBackgroundResult as referenceBackgroundResult6
|
|
16148
16148
|
} from "@hyperframes/engine";
|
|
16149
|
-
import { mkdirSync as mkdirSync20, mkdtempSync
|
|
16150
|
-
import { join as
|
|
16149
|
+
import { mkdirSync as mkdirSync20, mkdtempSync, readFileSync as readFileSync14, rmSync as rmSync18 } from "node:fs";
|
|
16150
|
+
import { join as join44 } from "node:path";
|
|
16151
16151
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
16152
16152
|
|
|
16153
16153
|
// src/services/distributed/mp4Stitch.ts
|
|
16154
|
-
import { createReadStream as createReadStream3, createWriteStream, mkdirSync as mkdirSync13,
|
|
16154
|
+
import { createReadStream as createReadStream3, createWriteStream, mkdirSync as mkdirSync13, rmSync as rmSync13 } from "node:fs";
|
|
16155
16155
|
import { open } from "node:fs/promises";
|
|
16156
16156
|
import { renameWithRetry as renameWithRetry7 } from "@hyperframes/engine";
|
|
16157
|
-
import { dirname as dirname14, join as
|
|
16157
|
+
import { basename as basename7, dirname as dirname14, join as join31 } from "node:path";
|
|
16158
16158
|
import { finished, pipeline } from "node:stream/promises";
|
|
16159
|
+
import { createStagingDir as createStagingDir4 } from "@hyperframes/core/atomic-file";
|
|
16159
16160
|
function boxAt(src, off, at, limit) {
|
|
16160
16161
|
let size = src.readUInt32BE(off);
|
|
16161
16162
|
let bodyStart = at + 8;
|
|
@@ -16519,14 +16520,14 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
16519
16520
|
const layouts = await Promise.all(sourcePaths.map((path) => readMp4Layout(fileRange(path))));
|
|
16520
16521
|
const header = audioPath ? buildStitchedHeader(layouts.slice(1), layouts[0]) : buildStitchedHeader(layouts);
|
|
16521
16522
|
mkdirSync13(dirname14(outputPath), { recursive: true });
|
|
16522
|
-
const workDir =
|
|
16523
|
-
const out = createWriteStream(
|
|
16523
|
+
const workDir = createStagingDir4(dirname14(outputPath), `${basename7(outputPath)}.stitch-`);
|
|
16524
|
+
const out = createWriteStream(join31(workDir, "out.mp4"));
|
|
16524
16525
|
try {
|
|
16525
16526
|
out.write(header);
|
|
16526
16527
|
for (const [i, path] of sourcePaths.entries()) await copyPayload(path, layouts[i], i, out);
|
|
16527
16528
|
out.end();
|
|
16528
16529
|
await finished(out);
|
|
16529
|
-
renameWithRetry7(
|
|
16530
|
+
renameWithRetry7(join31(workDir, "out.mp4"), outputPath);
|
|
16530
16531
|
} finally {
|
|
16531
16532
|
out.destroy();
|
|
16532
16533
|
rmSync13(workDir, { recursive: true, force: true });
|
|
@@ -16549,7 +16550,7 @@ import {
|
|
|
16549
16550
|
rmSync as rmSync14,
|
|
16550
16551
|
writeFileSync as writeFileSync10
|
|
16551
16552
|
} from "node:fs";
|
|
16552
|
-
import { extname as extname6, isAbsolute as isAbsolute3, join as
|
|
16553
|
+
import { extname as extname6, isAbsolute as isAbsolute3, join as join39, relative as relative5, sep as sep3 } from "node:path";
|
|
16553
16554
|
import {
|
|
16554
16555
|
fpsToNumber as fpsToNumber9,
|
|
16555
16556
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
@@ -16565,7 +16566,7 @@ import {
|
|
|
16565
16566
|
} from "@hyperframes/engine";
|
|
16566
16567
|
|
|
16567
16568
|
// src/services/distributed/planFrameCost.ts
|
|
16568
|
-
import { join as
|
|
16569
|
+
import { join as join34 } from "node:path";
|
|
16569
16570
|
import {
|
|
16570
16571
|
closeCaptureSession as closeCaptureSession9,
|
|
16571
16572
|
createCaptureSession as createCaptureSession9,
|
|
@@ -16575,7 +16576,7 @@ import {
|
|
|
16575
16576
|
|
|
16576
16577
|
// src/services/distributed/chunkFrames.ts
|
|
16577
16578
|
import { existsSync as existsSync23, readdirSync as readdirSync7 } from "node:fs";
|
|
16578
|
-
import { extname as extname4, join as
|
|
16579
|
+
import { extname as extname4, join as join33 } from "node:path";
|
|
16579
16580
|
import {
|
|
16580
16581
|
compositionRequiresWebGpu as compositionRequiresWebGpu3,
|
|
16581
16582
|
createVideoFrameInjector as createVideoFrameInjector3
|
|
@@ -16583,7 +16584,7 @@ import {
|
|
|
16583
16584
|
|
|
16584
16585
|
// src/services/distributed/shared.ts
|
|
16585
16586
|
import { execFile as execFileCallback } from "node:child_process";
|
|
16586
|
-
import { dirname as dirname15, join as
|
|
16587
|
+
import { dirname as dirname15, join as join32 } from "node:path";
|
|
16587
16588
|
import { existsSync as existsSync22, readFileSync as readFileSync9 } from "node:fs";
|
|
16588
16589
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
16589
16590
|
import { promisify as promisify2 } from "node:util";
|
|
@@ -16599,7 +16600,7 @@ function isPlanAudioArtifactPath(path) {
|
|
|
16599
16600
|
}
|
|
16600
16601
|
function resolvePlanAudioPath(planDir) {
|
|
16601
16602
|
for (const name of [PLAN_AUDIO_RELATIVE_PATH, PLAN_AUDIO_LEGACY_RELATIVE_PATH]) {
|
|
16602
|
-
const candidate =
|
|
16603
|
+
const candidate = join32(planDir, name);
|
|
16603
16604
|
if (existsSync22(candidate)) return candidate;
|
|
16604
16605
|
}
|
|
16605
16606
|
return null;
|
|
@@ -16850,7 +16851,7 @@ function buildSyntheticRenderJob(input) {
|
|
|
16850
16851
|
strictness: input.strictness,
|
|
16851
16852
|
producerConfig: input.producerConfig,
|
|
16852
16853
|
variables: input.variables,
|
|
16853
|
-
pageMotionBlur:
|
|
16854
|
+
pageMotionBlur: input.pageMotionBlur
|
|
16854
16855
|
};
|
|
16855
16856
|
return createRenderJob(renderConfig);
|
|
16856
16857
|
}
|
|
@@ -16860,7 +16861,7 @@ function readProducerVersion() {
|
|
|
16860
16861
|
const startDir = dirname15(fileURLToPath5(import.meta.url));
|
|
16861
16862
|
let current = startDir;
|
|
16862
16863
|
for (let i = 0; i < 10; i++) {
|
|
16863
|
-
const candidate =
|
|
16864
|
+
const candidate = join32(current, "package.json");
|
|
16864
16865
|
if (existsSync22(candidate)) {
|
|
16865
16866
|
try {
|
|
16866
16867
|
const pkg = JSON.parse(readFileSync9(candidate, "utf-8"));
|
|
@@ -16915,7 +16916,7 @@ function frameNumberFromFileName(name) {
|
|
|
16915
16916
|
function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v1") {
|
|
16916
16917
|
const result = [];
|
|
16917
16918
|
for (const v of videos) {
|
|
16918
|
-
const outputDir =
|
|
16919
|
+
const outputDir = join33(planDir, "video-frames", v.videoId);
|
|
16919
16920
|
if (!existsSync23(outputDir)) {
|
|
16920
16921
|
throw new Error(
|
|
16921
16922
|
`[renderChunk] planDir missing extracted video frames for ${JSON.stringify(v.videoId)}: ${outputDir} not present. plan() should have written frames here; the planDir is malformed.`
|
|
@@ -16934,7 +16935,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos, indexMode = "dense-v
|
|
|
16934
16935
|
if (!frameName) continue;
|
|
16935
16936
|
const frameNumber = indexMode === "sparse-v2" ? frameNumberFromFileName(frameName) : null;
|
|
16936
16937
|
const frameIndex = frameNumber === null ? i : frameNumber - 1;
|
|
16937
|
-
framePaths.set(frameIndex,
|
|
16938
|
+
framePaths.set(frameIndex, join33(outputDir, frameName));
|
|
16938
16939
|
}
|
|
16939
16940
|
result.push({
|
|
16940
16941
|
videoId: v.videoId,
|
|
@@ -16967,7 +16968,7 @@ function buildChunkCaptureOptions(input) {
|
|
|
16967
16968
|
skipReadinessVideoIds: input.skipReadinessVideoIds,
|
|
16968
16969
|
videoMetadataHints: input.videoMetadataHints,
|
|
16969
16970
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
16970
|
-
pageMotionBlur: encoder.pageMotionBlur
|
|
16971
|
+
pageMotionBlur: encoder.pageMotionBlur,
|
|
16971
16972
|
// A fixed warmup count keeps `beginFrameTimeTicks` host-independent.
|
|
16972
16973
|
lockWarmupTicks: true,
|
|
16973
16974
|
requiresWebGpu: compositionRequiresWebGpu3(input.html)
|
|
@@ -17014,7 +17015,7 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
17014
17015
|
sourceOf: (video) => {
|
|
17015
17016
|
const range = planned.get(video.videoId).deferredRange;
|
|
17016
17017
|
const remoteUrl = remoteSources[range.sourcePath];
|
|
17017
|
-
const sourcePath = remoteUrl ??
|
|
17018
|
+
const sourcePath = remoteUrl ?? join33(input.planDir, ...range.sourcePath.split("/"));
|
|
17018
17019
|
if (remoteUrl === void 0 && !existsSync23(sourcePath)) {
|
|
17019
17020
|
throw new RenderChunkValidationError(
|
|
17020
17021
|
MISSING_PLAN_ARTIFACT,
|
|
@@ -17045,12 +17046,12 @@ async function measurePlanSecondsPerFrame(input) {
|
|
|
17045
17046
|
planVideos,
|
|
17046
17047
|
frames: sampled,
|
|
17047
17048
|
fps: fpsRatio,
|
|
17048
|
-
outputRoot:
|
|
17049
|
+
outputRoot: join34(workDir, "frame-cost-video-frames"),
|
|
17049
17050
|
cfg: input.cfg
|
|
17050
17051
|
})
|
|
17051
17052
|
];
|
|
17052
17053
|
const lookup = extracted.length > 0 ? createFrameLookupTable4(planVideos.videos, extracted, resolveRenderFpsConfig(fpsRatio).value) : null;
|
|
17053
|
-
const compiledDir =
|
|
17054
|
+
const compiledDir = join34(planDir, "compiled");
|
|
17054
17055
|
const fileServer = await createFileServer({
|
|
17055
17056
|
projectDir: compiledDir,
|
|
17056
17057
|
compiledDir,
|
|
@@ -17062,7 +17063,7 @@ async function measurePlanSecondsPerFrame(input) {
|
|
|
17062
17063
|
try {
|
|
17063
17064
|
session = await createCaptureSession9(
|
|
17064
17065
|
fileServer.url,
|
|
17065
|
-
|
|
17066
|
+
join34(workDir, "frame-cost"),
|
|
17066
17067
|
buildChunkCaptureOptions({
|
|
17067
17068
|
dimensions: { ...input, fpsNum: fps, fpsDen: 1 },
|
|
17068
17069
|
encoder: input,
|
|
@@ -17159,7 +17160,7 @@ function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
|
17159
17160
|
}
|
|
17160
17161
|
|
|
17161
17162
|
// src/services/render/planReferenceBackground.ts
|
|
17162
|
-
import { join as
|
|
17163
|
+
import { join as join35 } from "node:path";
|
|
17163
17164
|
import {
|
|
17164
17165
|
closeCaptureSession as closeCaptureSession10,
|
|
17165
17166
|
createCaptureSession as createCaptureSession10,
|
|
@@ -17172,14 +17173,14 @@ async function readPlanReferenceBackground(input) {
|
|
|
17172
17173
|
try {
|
|
17173
17174
|
server = await createFileServer({
|
|
17174
17175
|
projectDir: input.projectDir,
|
|
17175
|
-
compiledDir:
|
|
17176
|
+
compiledDir: join35(input.workDir, "compiled"),
|
|
17176
17177
|
port: 0,
|
|
17177
17178
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
17178
17179
|
fps: input.options.fps
|
|
17179
17180
|
});
|
|
17180
17181
|
session = await createCaptureSession10(
|
|
17181
17182
|
server.url,
|
|
17182
|
-
|
|
17183
|
+
join35(input.workDir, "reference"),
|
|
17183
17184
|
{
|
|
17184
17185
|
...input.options,
|
|
17185
17186
|
requiresWebGpu: false
|
|
@@ -17216,7 +17217,7 @@ async function readPlanReferenceBackground(input) {
|
|
|
17216
17217
|
|
|
17217
17218
|
// src/services/render/stages/freezePlan.ts
|
|
17218
17219
|
import { existsSync as existsSync24, mkdirSync as mkdirSync14, readFileSync as readFileSync10, readdirSync as readdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
17219
|
-
import { join as
|
|
17220
|
+
import { join as join36, relative as relative3, resolve as resolve9 } from "node:path";
|
|
17220
17221
|
import {
|
|
17221
17222
|
REFERENCE_BACKGROUND_FIELD,
|
|
17222
17223
|
referenceBackgroundResult as referenceBackgroundResult3
|
|
@@ -17434,7 +17435,7 @@ function listPlanFiles(planDir) {
|
|
|
17434
17435
|
function walk(dir) {
|
|
17435
17436
|
const entries = readdirSync8(dir, { withFileTypes: true });
|
|
17436
17437
|
for (const entry of entries) {
|
|
17437
|
-
const full =
|
|
17438
|
+
const full = join36(dir, entry.name);
|
|
17438
17439
|
if (entry.isDirectory()) {
|
|
17439
17440
|
walk(full);
|
|
17440
17441
|
} else if (entry.isFile()) {
|
|
@@ -17475,15 +17476,15 @@ var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
|
|
|
17475
17476
|
function writePlanRemoteFiles(planDir, files) {
|
|
17476
17477
|
if (files.length === 0) return;
|
|
17477
17478
|
const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
|
|
17478
|
-
writeFileSync8(
|
|
17479
|
+
writeFileSync8(join36(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
|
|
17479
17480
|
}
|
|
17480
17481
|
function readPlanRemoteFiles(planDir) {
|
|
17481
|
-
const path =
|
|
17482
|
+
const path = join36(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
17482
17483
|
return existsSync24(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
17483
17484
|
}
|
|
17484
17485
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
17485
|
-
const planJsonPath =
|
|
17486
|
-
const encoderJsonPath =
|
|
17486
|
+
const planJsonPath = join36(planDir, "plan.json");
|
|
17487
|
+
const encoderJsonPath = join36(planDir, "meta", "encoder.json");
|
|
17487
17488
|
if (!existsSync24(planJsonPath)) {
|
|
17488
17489
|
throw new Error(`[freezePlan] plan.json missing: ${planJsonPath}`);
|
|
17489
17490
|
}
|
|
@@ -17520,18 +17521,18 @@ async function freezePlan(input) {
|
|
|
17520
17521
|
if (!existsSync24(planDir)) {
|
|
17521
17522
|
throw new Error(`[freezePlan] planDir does not exist: ${planDir}`);
|
|
17522
17523
|
}
|
|
17523
|
-
const metaDir =
|
|
17524
|
+
const metaDir = join36(planDir, "meta");
|
|
17524
17525
|
if (!existsSync24(metaDir)) mkdirSync14(metaDir, { recursive: true });
|
|
17525
17526
|
writeFileSync8(
|
|
17526
|
-
|
|
17527
|
+
join36(metaDir, "composition.json"),
|
|
17527
17528
|
`${JSON.stringify(composition, null, 2)}
|
|
17528
17529
|
`,
|
|
17529
17530
|
"utf-8"
|
|
17530
17531
|
);
|
|
17531
17532
|
const encoderForCanonical = stripUndefined(encoder);
|
|
17532
17533
|
const encoderConfigCanonicalJson = canonicalJsonStringify(encoderForCanonical);
|
|
17533
|
-
writeFileSync8(
|
|
17534
|
-
writeFileSync8(
|
|
17534
|
+
writeFileSync8(join36(metaDir, "encoder.json"), encoderConfigCanonicalJson, "utf-8");
|
|
17535
|
+
writeFileSync8(join36(metaDir, "chunks.json"), `${JSON.stringify(chunks, null, 2)}
|
|
17535
17536
|
`, "utf-8");
|
|
17536
17537
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
17537
17538
|
const planHash = computePlanHash({
|
|
@@ -17556,7 +17557,7 @@ async function freezePlan(input) {
|
|
|
17556
17557
|
duration: durationSeconds,
|
|
17557
17558
|
hasAudio
|
|
17558
17559
|
};
|
|
17559
|
-
const planJsonPath =
|
|
17560
|
+
const planJsonPath = join36(planDir, "plan.json");
|
|
17560
17561
|
writeFileSync8(planJsonPath, `${JSON.stringify(planJson, null, 2)}
|
|
17561
17562
|
`, "utf-8");
|
|
17562
17563
|
return { planJsonPath, planHash };
|
|
@@ -17600,7 +17601,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
17600
17601
|
// src/services/distributed/chunkAudio.ts
|
|
17601
17602
|
import { closeSync as closeSync6, existsSync as existsSync25, ftruncateSync, mkdirSync as mkdirSync15, openSync as openSync6, readFileSync as readFileSync11 } from "node:fs";
|
|
17602
17603
|
import { writeFileSync as writeFileSync9, writeSync } from "node:fs";
|
|
17603
|
-
import { join as
|
|
17604
|
+
import { join as join37 } from "node:path";
|
|
17604
17605
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
17605
17606
|
function audioCutForChunk(grid, chunks, index, fps) {
|
|
17606
17607
|
const firstPacketAt = (frame) => Math.min(
|
|
@@ -17688,7 +17689,7 @@ async function writeChunkAudioSlices(input) {
|
|
|
17688
17689
|
});
|
|
17689
17690
|
const bytes = readFileSync11(input.audioPath);
|
|
17690
17691
|
const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
|
|
17691
|
-
mkdirSync15(
|
|
17692
|
+
mkdirSync15(join37(input.planDir, SLICE_DIR), { recursive: true });
|
|
17692
17693
|
input.chunks.forEach((_, index) => {
|
|
17693
17694
|
const cut = audioCutForChunk(grid, input.chunks, index, input.fps);
|
|
17694
17695
|
if (!cut) return;
|
|
@@ -17696,17 +17697,17 @@ async function writeChunkAudioSlices(input) {
|
|
|
17696
17697
|
const start = offsets[cut.firstPacket];
|
|
17697
17698
|
layout.partStarts[index] = start;
|
|
17698
17699
|
const end = offsets[last] + track.sizes[last];
|
|
17699
|
-
writeFileSync9(
|
|
17700
|
+
writeFileSync9(join37(input.planDir, chunkAudioSlicePath(index)), bytes.subarray(start, end));
|
|
17700
17701
|
});
|
|
17701
17702
|
const frame = [bytes.subarray(0, payloadStart), bytes.subarray(payloadEnd)];
|
|
17702
|
-
writeFileSync9(
|
|
17703
|
-
writeFileSync9(
|
|
17703
|
+
writeFileSync9(join37(input.planDir, SLICE_FRAME), Buffer.concat(frame));
|
|
17704
|
+
writeFileSync9(join37(input.planDir, SLICE_LAYOUT), JSON.stringify(layout));
|
|
17704
17705
|
}
|
|
17705
17706
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
17706
|
-
const layoutPath =
|
|
17707
|
+
const layoutPath = join37(planDir, SLICE_LAYOUT);
|
|
17707
17708
|
if (!existsSync25(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
17708
17709
|
const layout = JSON.parse(readFileSync11(layoutPath, "utf-8"));
|
|
17709
|
-
const frame = readFileSync11(
|
|
17710
|
+
const frame = readFileSync11(join37(planDir, SLICE_FRAME));
|
|
17710
17711
|
const fd = openSync6(scratchPath, "w");
|
|
17711
17712
|
try {
|
|
17712
17713
|
writeSync(fd, frame, 0, layout.payloadStart, 0);
|
|
@@ -17719,7 +17720,7 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
|
17719
17720
|
);
|
|
17720
17721
|
const start = layout.partStarts[chunkIndex];
|
|
17721
17722
|
if (start !== void 0) {
|
|
17722
|
-
const part = readFileSync11(
|
|
17723
|
+
const part = readFileSync11(join37(planDir, chunkAudioSlicePath(chunkIndex)));
|
|
17723
17724
|
writeSync(fd, part, 0, part.length, start);
|
|
17724
17725
|
}
|
|
17725
17726
|
ftruncateSync(fd, layout.sizeBytes);
|
|
@@ -17775,7 +17776,7 @@ async function muxChunkWithAudio(input) {
|
|
|
17775
17776
|
// src/services/distributed/planSize.ts
|
|
17776
17777
|
import { createHash as createHash9 } from "node:crypto";
|
|
17777
17778
|
import { lstatSync as lstatSync2, readdirSync as readdirSync9 } from "node:fs";
|
|
17778
|
-
import { extname as extname5, join as
|
|
17779
|
+
import { extname as extname5, join as join38, relative as relative4 } from "node:path";
|
|
17779
17780
|
var MAX_TOP_COMPONENTS = 10;
|
|
17780
17781
|
var SOURCE_MEDIA_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
17781
17782
|
".aac",
|
|
@@ -17887,7 +17888,7 @@ function walkRegularFiles(dir, visit) {
|
|
|
17887
17888
|
return;
|
|
17888
17889
|
}
|
|
17889
17890
|
for (const entry of entries) {
|
|
17890
|
-
const full =
|
|
17891
|
+
const full = join38(dir, entry.name);
|
|
17891
17892
|
if (entry.isDirectory()) {
|
|
17892
17893
|
walkRegularFiles(full, visit);
|
|
17893
17894
|
} else if (entry.isFile()) {
|
|
@@ -18103,13 +18104,13 @@ function assertPositiveInteger2(name, value) {
|
|
|
18103
18104
|
}
|
|
18104
18105
|
var FREEZE_OWNED_PLAN_FILES = [
|
|
18105
18106
|
"plan.json",
|
|
18106
|
-
|
|
18107
|
-
|
|
18108
|
-
|
|
18107
|
+
join39("meta", "composition.json"),
|
|
18108
|
+
join39("meta", "encoder.json"),
|
|
18109
|
+
join39("meta", "chunks.json")
|
|
18109
18110
|
];
|
|
18110
18111
|
function removeFreezeOwnedPlanFiles(planDir) {
|
|
18111
18112
|
for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
|
|
18112
|
-
rmSync14(
|
|
18113
|
+
rmSync14(join39(planDir, relativePath), { force: true });
|
|
18113
18114
|
}
|
|
18114
18115
|
}
|
|
18115
18116
|
function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
@@ -18264,19 +18265,19 @@ function createVideoSourceShipper(input) {
|
|
|
18264
18265
|
const known = shipped.get(srcPath);
|
|
18265
18266
|
if (known) return known;
|
|
18266
18267
|
const stagedRel = pathInside(input.workVideoFramesDir, srcPath);
|
|
18267
|
-
const current = stagedRel === null ? srcPath :
|
|
18268
|
+
const current = stagedRel === null ? srcPath : join39(input.planDir, "video-frames", stagedRel);
|
|
18268
18269
|
let result = null;
|
|
18269
18270
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
18270
18271
|
const rel = pathInside(root, srcPath);
|
|
18271
|
-
if (rel !== null && (existsSync26(
|
|
18272
|
+
if (rel !== null && (existsSync26(join39(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
18272
18273
|
result = ["compiled", ...rel.split(sep3)].join("/");
|
|
18273
18274
|
break;
|
|
18274
18275
|
}
|
|
18275
18276
|
}
|
|
18276
18277
|
if (result === null) {
|
|
18277
18278
|
result = `video-sources/${shipped.size}${extname6(current)}`;
|
|
18278
|
-
const destination =
|
|
18279
|
-
mkdirSync16(
|
|
18279
|
+
const destination = join39(input.planDir, ...result.split("/"));
|
|
18280
|
+
mkdirSync16(join39(input.planDir, "video-sources"), { recursive: true });
|
|
18280
18281
|
try {
|
|
18281
18282
|
linkSync(current, destination);
|
|
18282
18283
|
} catch {
|
|
@@ -18322,17 +18323,18 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
18322
18323
|
entryFile: config.entryFile ?? "index.html",
|
|
18323
18324
|
logger: config.logger,
|
|
18324
18325
|
producerConfig: cfg,
|
|
18325
|
-
variables: config.variables
|
|
18326
|
+
variables: config.variables,
|
|
18327
|
+
pageMotionBlur: config.pageMotionBlur
|
|
18326
18328
|
});
|
|
18327
18329
|
const entryFile = config.entryFile ?? "index.html";
|
|
18328
|
-
const htmlPath =
|
|
18330
|
+
const htmlPath = join39(projectDir, entryFile);
|
|
18329
18331
|
if (!existsSync26(htmlPath)) {
|
|
18330
18332
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
18331
18333
|
}
|
|
18332
|
-
const workDir =
|
|
18334
|
+
const workDir = join39(planDir, ".plan-work");
|
|
18333
18335
|
rmSync14(workDir, { recursive: true, force: true });
|
|
18334
18336
|
mkdirSync16(workDir, { recursive: true });
|
|
18335
|
-
const compiledDir =
|
|
18337
|
+
const compiledDir = join39(workDir, "compiled");
|
|
18336
18338
|
mkdirSync16(compiledDir, { recursive: true });
|
|
18337
18339
|
let stageStarted = Date.now();
|
|
18338
18340
|
cpSync2(projectDir, compiledDir, {
|
|
@@ -18346,10 +18348,10 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
18346
18348
|
}
|
|
18347
18349
|
});
|
|
18348
18350
|
logPlanStage(log, "copy", stageStarted);
|
|
18349
|
-
const finalCompiledDir =
|
|
18351
|
+
const finalCompiledDir = join39(planDir, "compiled");
|
|
18350
18352
|
const needsAlpha = outputNeedsAlpha(config.format);
|
|
18351
18353
|
const remoteFiles = Object.entries(options.remoteFiles ?? {});
|
|
18352
|
-
const remoteLocalPath = (rel) =>
|
|
18354
|
+
const remoteLocalPath = (rel) => join39(projectDir, ...rel.split("/"));
|
|
18353
18355
|
for (const [, file] of remoteFiles) {
|
|
18354
18356
|
declareContentAddressedUrl(file.url, file.sha256);
|
|
18355
18357
|
if (file.probeFactsUrl) declareProbeFactsUrl(file.url, file.probeFactsUrl);
|
|
@@ -18552,10 +18554,10 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18552
18554
|
let stageStarted = Date.now();
|
|
18553
18555
|
const extractResult = await extractPlanVideos(c, options.deferVideoExtraction);
|
|
18554
18556
|
logPlanStage(log, "extract", stageStarted);
|
|
18555
|
-
const stagedVideoFrames =
|
|
18556
|
-
const videoFramesDst =
|
|
18557
|
+
const stagedVideoFrames = join39(compiledDir, "__hyperframes_video_frames");
|
|
18558
|
+
const videoFramesDst = join39(planDir, "video-frames");
|
|
18557
18559
|
if (existsSync26(videoFramesDst)) rmSync14(videoFramesDst, { recursive: true, force: true });
|
|
18558
|
-
const videoSourcesDir =
|
|
18560
|
+
const videoSourcesDir = join39(planDir, "video-sources");
|
|
18559
18561
|
rmSync14(videoSourcesDir, { recursive: true, force: true });
|
|
18560
18562
|
if (existsSync26(stagedVideoFrames)) {
|
|
18561
18563
|
renameWithRetry8(stagedVideoFrames, videoFramesDst);
|
|
@@ -18592,7 +18594,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18592
18594
|
};
|
|
18593
18595
|
})
|
|
18594
18596
|
});
|
|
18595
|
-
mkdirSync16(
|
|
18597
|
+
mkdirSync16(join39(planDir, "meta"), { recursive: true });
|
|
18596
18598
|
writePlanRemoteFiles(
|
|
18597
18599
|
planDir,
|
|
18598
18600
|
remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
|
|
@@ -18602,7 +18604,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18602
18604
|
}))
|
|
18603
18605
|
);
|
|
18604
18606
|
writeFileSync10(
|
|
18605
|
-
|
|
18607
|
+
join39(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
|
|
18606
18608
|
JSON.stringify(planVideosJson, null, 2),
|
|
18607
18609
|
"utf-8"
|
|
18608
18610
|
);
|
|
@@ -18682,7 +18684,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
18682
18684
|
if (audioResult?.audioError) {
|
|
18683
18685
|
applyDistributedAudioWarningPolicy(job, audioResult.audioError, audioResult.audioFailures, log);
|
|
18684
18686
|
}
|
|
18685
|
-
const planAudioPath =
|
|
18687
|
+
const planAudioPath = join39(planDir, PLAN_AUDIO_RELATIVE_PATH);
|
|
18686
18688
|
if (audioResult?.hasAudio && existsSync26(audioResult.audioOutputPath)) {
|
|
18687
18689
|
renameWithRetry8(audioResult.audioOutputPath, planAudioPath);
|
|
18688
18690
|
}
|
|
@@ -18806,7 +18808,7 @@ async function plan(projectDir, config, planDir, options = {}) {
|
|
|
18806
18808
|
import { randomBytes } from "node:crypto";
|
|
18807
18809
|
import { existsSync as existsSync29, mkdirSync as mkdirSync19, readFileSync as readFileSync13, readdirSync as readdirSync11, rmSync as rmSync17, writeFileSync as writeFileSync13 } from "node:fs";
|
|
18808
18810
|
import { cpus as cpus3 } from "node:os";
|
|
18809
|
-
import { join as
|
|
18811
|
+
import { join as join43 } from "node:path";
|
|
18810
18812
|
import {
|
|
18811
18813
|
assertSwiftShader,
|
|
18812
18814
|
BROWSER_GPU_NOT_SOFTWARE as BROWSER_GPU_NOT_SOFTWARE4,
|
|
@@ -18834,7 +18836,6 @@ import {
|
|
|
18834
18836
|
existsSync as existsSync28,
|
|
18835
18837
|
lstatSync as lstatSync3,
|
|
18836
18838
|
mkdirSync as mkdirSync18,
|
|
18837
|
-
mkdtempSync as mkdtempSync6,
|
|
18838
18839
|
readFileSync as readFileSync12,
|
|
18839
18840
|
readdirSync as readdirSync10,
|
|
18840
18841
|
rmSync as rmSync16,
|
|
@@ -18842,7 +18843,7 @@ import {
|
|
|
18842
18843
|
writeFileSync as writeFileSync12
|
|
18843
18844
|
} from "node:fs";
|
|
18844
18845
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
18845
|
-
import { dirname as dirname17, isAbsolute as isAbsolute4, join as
|
|
18846
|
+
import { dirname as dirname17, isAbsolute as isAbsolute4, join as join42, relative as relative6, resolve as resolve10, sep as sep4 } from "node:path";
|
|
18846
18847
|
import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
18847
18848
|
import { createFrameLookupTable as createFrameLookupTable5, renameWithRetry as renameWithRetry10 } from "@hyperframes/engine";
|
|
18848
18849
|
|
|
@@ -18852,13 +18853,12 @@ import {
|
|
|
18852
18853
|
existsSync as existsSync27,
|
|
18853
18854
|
linkSync as linkSync2,
|
|
18854
18855
|
mkdirSync as mkdirSync17,
|
|
18855
|
-
mkdtempSync as mkdtempSync5,
|
|
18856
18856
|
rmSync as rmSync15,
|
|
18857
18857
|
statSync as statSync10,
|
|
18858
18858
|
writeFileSync as writeFileSync11
|
|
18859
18859
|
} from "node:fs";
|
|
18860
18860
|
import { renameWithRetry as renameWithRetry9 } from "@hyperframes/engine";
|
|
18861
|
-
import { dirname as dirname16, join as
|
|
18861
|
+
import { dirname as dirname16, join as join41 } from "node:path";
|
|
18862
18862
|
|
|
18863
18863
|
// src/services/distributed/planV2Errors.ts
|
|
18864
18864
|
var PLAN_V2_INTEGRITY_UNRECOVERABLE = "PLAN_V2_INTEGRITY_UNRECOVERABLE";
|
|
@@ -18873,7 +18873,7 @@ var PlanV2IntegrityError = class extends Error {
|
|
|
18873
18873
|
};
|
|
18874
18874
|
|
|
18875
18875
|
// src/services/distributed/planV2Layout.ts
|
|
18876
|
-
import { join as
|
|
18876
|
+
import { join as join40 } from "node:path";
|
|
18877
18877
|
var SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
18878
18878
|
function assertPlanV2Sha256(value, field) {
|
|
18879
18879
|
if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
|
|
@@ -18883,10 +18883,11 @@ function assertPlanV2Sha256(value, field) {
|
|
|
18883
18883
|
}
|
|
18884
18884
|
function planV2BlobPath(root, sha256) {
|
|
18885
18885
|
const digest = assertPlanV2Sha256(sha256, "artifact sha256");
|
|
18886
|
-
return
|
|
18886
|
+
return join40(root, "artifacts", "sha256", digest.slice(0, 2), digest);
|
|
18887
18887
|
}
|
|
18888
18888
|
|
|
18889
18889
|
// src/services/distributed/planV2Publisher.ts
|
|
18890
|
+
import { createStagingDir as createStagingDir5 } from "@hyperframes/core/atomic-file";
|
|
18890
18891
|
function canFallbackToCopy(error) {
|
|
18891
18892
|
if (!(error instanceof Error) || !("code" in error)) return false;
|
|
18892
18893
|
const code = error.code;
|
|
@@ -18924,7 +18925,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18924
18925
|
this.destinationDir = destinationDir;
|
|
18925
18926
|
this.#linkFile = options.linkFile ?? linkSync2;
|
|
18926
18927
|
mkdirSync17(dirname16(destinationDir), { recursive: true });
|
|
18927
|
-
this.temporaryDir =
|
|
18928
|
+
this.temporaryDir = createStagingDir5(dirname16(destinationDir), ".plan-v2-publish-");
|
|
18928
18929
|
}
|
|
18929
18930
|
async putBlob(blob) {
|
|
18930
18931
|
const digest = assertPlanV2Sha256(blob.sha256, "published blob sha256");
|
|
@@ -18937,8 +18938,8 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18937
18938
|
const destinationPath = planV2BlobPath(this.temporaryDir, digest);
|
|
18938
18939
|
if (existsSync27(destinationPath)) return;
|
|
18939
18940
|
mkdirSync17(dirname16(destinationPath), { recursive: true });
|
|
18940
|
-
const stagingDir =
|
|
18941
|
-
const temporaryPath =
|
|
18941
|
+
const stagingDir = createStagingDir5(dirname16(destinationPath), ".plan-v2-blob-");
|
|
18942
|
+
const temporaryPath = join41(stagingDir, "blob");
|
|
18942
18943
|
try {
|
|
18943
18944
|
try {
|
|
18944
18945
|
this.#linkFile(blob.sourcePath, temporaryPath);
|
|
@@ -18959,7 +18960,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18959
18960
|
);
|
|
18960
18961
|
}
|
|
18961
18962
|
}
|
|
18962
|
-
writeFileSync11(
|
|
18963
|
+
writeFileSync11(join41(this.temporaryDir, "plan.json"), manifestBytes, "utf-8");
|
|
18963
18964
|
renameWithRetry9(this.temporaryDir, this.destinationDir);
|
|
18964
18965
|
this.#committed = true;
|
|
18965
18966
|
}
|
|
@@ -18971,6 +18972,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
18971
18972
|
};
|
|
18972
18973
|
|
|
18973
18974
|
// src/services/distributed/planV2.ts
|
|
18975
|
+
import { createStagingDir as createStagingDir6 } from "@hyperframes/core/atomic-file";
|
|
18974
18976
|
var PLAN_V2_HASH_PREFIX = "hyperframes-plan-manifest-hash-v2\0";
|
|
18975
18977
|
var EXTRACTION_CACHE_COMPLETE_SENTINEL = ".hf-complete";
|
|
18976
18978
|
var PLAN_V2_MATERIALIZATION_MARKER = ".hyperframes-plan-v2.json";
|
|
@@ -19004,7 +19006,7 @@ function listFiles(root) {
|
|
|
19004
19006
|
const rootResolved = resolve10(root);
|
|
19005
19007
|
function walk(dir) {
|
|
19006
19008
|
for (const entry of readdirSync10(dir, { withFileTypes: true })) {
|
|
19007
|
-
const absolutePath =
|
|
19009
|
+
const absolutePath = join42(dir, entry.name);
|
|
19008
19010
|
if (entry.isDirectory()) {
|
|
19009
19011
|
walk(absolutePath);
|
|
19010
19012
|
} else if (entry.isFile()) {
|
|
@@ -19031,13 +19033,13 @@ function assertValidExtractionCacheCompleteSentinel(path) {
|
|
|
19031
19033
|
}
|
|
19032
19034
|
}
|
|
19033
19035
|
function validateExtractionCacheCompleteSentinels(executionPlanDir) {
|
|
19034
|
-
const videoRoot =
|
|
19036
|
+
const videoRoot = join42(executionPlanDir, "video-frames");
|
|
19035
19037
|
if (!existsSync28(videoRoot)) return;
|
|
19036
19038
|
for (const videoEntry of readdirSync10(videoRoot, { withFileTypes: true })) {
|
|
19037
19039
|
if (!videoEntry.isDirectory()) continue;
|
|
19038
|
-
const videoDir =
|
|
19040
|
+
const videoDir = join42(videoRoot, videoEntry.name);
|
|
19039
19041
|
if (readdirSync10(videoDir).includes(EXTRACTION_CACHE_COMPLETE_SENTINEL)) {
|
|
19040
|
-
const sentinelPath =
|
|
19042
|
+
const sentinelPath = join42(videoDir, EXTRACTION_CACHE_COMPLETE_SENTINEL);
|
|
19041
19043
|
assertValidExtractionCacheCompleteSentinel(sentinelPath);
|
|
19042
19044
|
}
|
|
19043
19045
|
}
|
|
@@ -19084,7 +19086,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
|
|
|
19084
19086
|
if (video.deferredRange) {
|
|
19085
19087
|
const framePaths2 = /* @__PURE__ */ new Map();
|
|
19086
19088
|
for (let index = 0; index < video.totalFrames; index++) {
|
|
19087
|
-
framePaths2.set(index,
|
|
19089
|
+
framePaths2.set(index, join42(outputDir, String(index)));
|
|
19088
19090
|
}
|
|
19089
19091
|
return { ...video, outputDir, framePaths: framePaths2, ownedByLookup: false };
|
|
19090
19092
|
}
|
|
@@ -19092,7 +19094,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
|
|
|
19092
19094
|
const framePaths = /* @__PURE__ */ new Map();
|
|
19093
19095
|
for (const frameName of frameNames) {
|
|
19094
19096
|
if (frameName === EXTRACTION_CACHE_COMPLETE_SENTINEL) {
|
|
19095
|
-
assertValidExtractionCacheCompleteSentinel(
|
|
19097
|
+
assertValidExtractionCacheCompleteSentinel(join42(outputDir, frameName));
|
|
19096
19098
|
continue;
|
|
19097
19099
|
}
|
|
19098
19100
|
const match = /(\d+)(?=\.[^.]+$)/.exec(frameName);
|
|
@@ -19111,7 +19113,7 @@ function listVideoFramePaths(executionPlanDir, videos) {
|
|
|
19111
19113
|
`duplicate extracted frame index ${frameIndex} in ${outputDir}`
|
|
19112
19114
|
);
|
|
19113
19115
|
}
|
|
19114
|
-
framePaths.set(frameIndex,
|
|
19116
|
+
framePaths.set(frameIndex, join42(outputDir, frameName));
|
|
19115
19117
|
}
|
|
19116
19118
|
return {
|
|
19117
19119
|
...video,
|
|
@@ -19132,7 +19134,7 @@ function parsePlanVideosJson2(value) {
|
|
|
19132
19134
|
}
|
|
19133
19135
|
}
|
|
19134
19136
|
function materializeExtractedVideoDirectories(planDir) {
|
|
19135
|
-
const videosPath =
|
|
19137
|
+
const videosPath = join42(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19136
19138
|
if (!existsSync28(videosPath)) return;
|
|
19137
19139
|
const videos = parsePlanVideosJson2(readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH));
|
|
19138
19140
|
for (const video of videos.extracted) {
|
|
@@ -19166,13 +19168,13 @@ function parseChunkSlices(value) {
|
|
|
19166
19168
|
});
|
|
19167
19169
|
}
|
|
19168
19170
|
function buildVideoChunkDependencies(executionPlanDir, dimensions) {
|
|
19169
|
-
const videoRoot =
|
|
19171
|
+
const videoRoot = join42(executionPlanDir, "video-frames");
|
|
19170
19172
|
const hasExtractedFrames = existsSync28(videoRoot) && listFiles(videoRoot).length > 0;
|
|
19171
|
-
const videosPath =
|
|
19173
|
+
const videosPath = join42(executionPlanDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19172
19174
|
if (!existsSync28(videosPath)) {
|
|
19173
19175
|
return hasExtractedFrames ? { mode: "full-source-pack", dependencies: null } : { mode: "exact-rendered-frames", dependencies: /* @__PURE__ */ new Map() };
|
|
19174
19176
|
}
|
|
19175
|
-
const chunksPath =
|
|
19177
|
+
const chunksPath = join42(executionPlanDir, "meta", "chunks.json");
|
|
19176
19178
|
const videos = readJsonFile(videosPath, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19177
19179
|
const chunks = readJsonFile(chunksPath, "meta/chunks.json");
|
|
19178
19180
|
const parsedVideos = parsePlanVideosJson2(videos);
|
|
@@ -19228,8 +19230,8 @@ function computeManifestHash(manifest) {
|
|
|
19228
19230
|
function writeBlob(sourcePath, destinationPath) {
|
|
19229
19231
|
if (existsSync28(destinationPath)) return;
|
|
19230
19232
|
mkdirSync18(dirname17(destinationPath), { recursive: true });
|
|
19231
|
-
const temporaryDir =
|
|
19232
|
-
const temporaryPath =
|
|
19233
|
+
const temporaryDir = createStagingDir6(dirname17(destinationPath), ".plan-v2-blob-");
|
|
19234
|
+
const temporaryPath = join42(temporaryDir, "blob");
|
|
19233
19235
|
try {
|
|
19234
19236
|
copyFileSync8(sourcePath, temporaryPath);
|
|
19235
19237
|
renameWithRetry10(temporaryPath, destinationPath);
|
|
@@ -19265,7 +19267,7 @@ async function putTasks(tasks) {
|
|
|
19265
19267
|
if (failure) throw failure.reason;
|
|
19266
19268
|
}
|
|
19267
19269
|
function buildPlanV2Publication(executionPlanDir, digests) {
|
|
19268
|
-
const executionPlanPath =
|
|
19270
|
+
const executionPlanPath = join42(executionPlanDir, "plan.json");
|
|
19269
19271
|
if (!existsSync28(executionPlanPath)) {
|
|
19270
19272
|
throw new PlanV2IntegrityError(`execution plan is missing plan.json: ${executionPlanPath}`);
|
|
19271
19273
|
}
|
|
@@ -19293,7 +19295,7 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
19293
19295
|
}
|
|
19294
19296
|
validateExtractionCacheCompleteSentinels(executionPlanDir);
|
|
19295
19297
|
const videoDependencyPlan = buildVideoChunkDependencies(executionPlanDir, dimensions);
|
|
19296
|
-
const encoder = readJsonFile(
|
|
19298
|
+
const encoder = readJsonFile(join42(executionPlanDir, "meta", "encoder.json"), "meta/encoder.json");
|
|
19297
19299
|
const files = listFiles(executionPlanDir);
|
|
19298
19300
|
const chunkAudio = !isRecord3(encoder) || encoder.chunksCarryAudio !== true ? "none" : files.some((file) => chunkAudioSliceTarget(file.path) !== null) ? "sliced" : "whole";
|
|
19299
19301
|
for (const file of files) {
|
|
@@ -19354,13 +19356,13 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
19354
19356
|
throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
|
|
19355
19357
|
}
|
|
19356
19358
|
mkdirSync18(dirname17(planV2Dir), { recursive: true });
|
|
19357
|
-
const tempDir =
|
|
19359
|
+
const tempDir = createStagingDir6(dirname17(planV2Dir), ".plan-v2-build-");
|
|
19358
19360
|
try {
|
|
19359
19361
|
for (const blob of publication.blobs) {
|
|
19360
19362
|
writeBlob(blob.sourcePath, planV2BlobPath(tempDir, blob.sha256));
|
|
19361
19363
|
}
|
|
19362
19364
|
writeFileSync12(
|
|
19363
|
-
|
|
19365
|
+
join42(tempDir, "plan.json"),
|
|
19364
19366
|
canonicalJsonStringify(publication.manifest),
|
|
19365
19367
|
"utf-8"
|
|
19366
19368
|
);
|
|
@@ -19401,7 +19403,7 @@ async function publishExecutionPlan(executionPlanDir, publisher, { digests, log
|
|
|
19401
19403
|
}
|
|
19402
19404
|
}
|
|
19403
19405
|
async function planV2WithPublisher(projectDir, config, publisher, options = {}) {
|
|
19404
|
-
const stagingRoot =
|
|
19406
|
+
const stagingRoot = createStagingDir6(options.stagingParentDir ?? tmpdir3(), ".execution-plan-");
|
|
19405
19407
|
const digests = /* @__PURE__ */ new Map();
|
|
19406
19408
|
try {
|
|
19407
19409
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
@@ -19412,7 +19414,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
19412
19414
|
// artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
|
|
19413
19415
|
whileAudioMixes: () => putBlobs(
|
|
19414
19416
|
publisher,
|
|
19415
|
-
listFiles(
|
|
19417
|
+
listFiles(join42(stagingRoot, "compiled")).map(
|
|
19416
19418
|
(file) => publishBlobFor(file.absolutePath, digests)
|
|
19417
19419
|
)
|
|
19418
19420
|
),
|
|
@@ -19449,7 +19451,7 @@ function resultFromManifest(planV2Dir, manifest) {
|
|
|
19449
19451
|
isRecord3(executionPlan) ? executionPlan[REFERENCE_BACKGROUND_FIELD3] : void 0
|
|
19450
19452
|
),
|
|
19451
19453
|
planDir: planV2Dir,
|
|
19452
|
-
manifestPath:
|
|
19454
|
+
manifestPath: join42(planV2Dir, "plan.json"),
|
|
19453
19455
|
planProtocol: PLAN_PROTOCOL_V2,
|
|
19454
19456
|
planHash: manifest.planHash,
|
|
19455
19457
|
sourcePlanV1Hash: getPlanV2ExecutionPlanHash(manifest),
|
|
@@ -19585,7 +19587,7 @@ function parsePlanV2Manifest(value) {
|
|
|
19585
19587
|
return parsed;
|
|
19586
19588
|
}
|
|
19587
19589
|
function readPlanV2Manifest(planV2Dir) {
|
|
19588
|
-
const manifestPath =
|
|
19590
|
+
const manifestPath = join42(planV2Dir, "plan.json");
|
|
19589
19591
|
if (!existsSync28(manifestPath)) {
|
|
19590
19592
|
throw new PlanV2IntegrityError(`missing v2 manifest: ${manifestPath}`);
|
|
19591
19593
|
}
|
|
@@ -19631,16 +19633,16 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
19631
19633
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
19632
19634
|
}));
|
|
19633
19635
|
mkdirSync18(dirname17(destinationDir), { recursive: true });
|
|
19634
|
-
const tempDir =
|
|
19636
|
+
const tempDir = createStagingDir6(dirname17(destinationDir), ".plan-v2-materialize-");
|
|
19635
19637
|
try {
|
|
19636
19638
|
for (const { artifact, sourcePath } of verified) {
|
|
19637
|
-
const destinationPath =
|
|
19639
|
+
const destinationPath = join42(tempDir, ...artifact.path.split("/"));
|
|
19638
19640
|
mkdirSync18(dirname17(destinationPath), { recursive: true });
|
|
19639
19641
|
copyFileSync8(sourcePath, destinationPath);
|
|
19640
19642
|
}
|
|
19641
19643
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
19642
19644
|
writeFileSync12(
|
|
19643
|
-
|
|
19645
|
+
join42(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
19644
19646
|
canonicalJsonStringify({
|
|
19645
19647
|
manifest,
|
|
19646
19648
|
target,
|
|
@@ -19663,7 +19665,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
19663
19665
|
// Join the artifact's OWN name, not the current constant: a plan written
|
|
19664
19666
|
// before the container change carries the legacy name, and materializing it
|
|
19665
19667
|
// under the new one would point at a file that was never written.
|
|
19666
|
-
audioPath: target.role === "assembler" ? audioArtifact ?
|
|
19668
|
+
audioPath: target.role === "assembler" ? audioArtifact ? join42(destinationDir, audioArtifact.path) : null : null
|
|
19667
19669
|
};
|
|
19668
19670
|
}
|
|
19669
19671
|
function assertRemoteSources(remoteSources, artifacts) {
|
|
@@ -19682,7 +19684,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
19682
19684
|
return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
|
|
19683
19685
|
}
|
|
19684
19686
|
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
19685
|
-
const markerPath =
|
|
19687
|
+
const markerPath = join42(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
19686
19688
|
if (!existsSync28(markerPath)) return null;
|
|
19687
19689
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
19688
19690
|
if (!isRecord3(marker) || !isRecord3(marker.manifest) || !isRecord3(marker.target)) {
|
|
@@ -19700,7 +19702,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
19700
19702
|
assertRemoteSources(remoteSources, required);
|
|
19701
19703
|
for (const artifact of required) {
|
|
19702
19704
|
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
19703
|
-
const path =
|
|
19705
|
+
const path = join42(planDir, ...artifact.path.split("/"));
|
|
19704
19706
|
if (!existsSync28(path) || statSync11(path).size !== artifact.sizeBytes) {
|
|
19705
19707
|
throw new PlanV2IntegrityError(
|
|
19706
19708
|
`materialized artifact missing or truncated: ${artifact.path}`
|
|
@@ -19832,7 +19834,7 @@ function hashChunkOutput(outputPath, kind) {
|
|
|
19832
19834
|
if (kind === "file") return sha256Hex(readFileSync13(outputPath));
|
|
19833
19835
|
const entries = readdirSync11(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
19834
19836
|
const lines = entries.map(
|
|
19835
|
-
(name) => `${name}\0${sha256Hex(readFileSync13(
|
|
19837
|
+
(name) => `${name}\0${sha256Hex(readFileSync13(join43(outputPath, name)))}`
|
|
19836
19838
|
);
|
|
19837
19839
|
return sha256Hex(lines.join("\0"));
|
|
19838
19840
|
}
|
|
@@ -19849,9 +19851,9 @@ function resolveLockedVp9CpuUsed(lockedEncoder) {
|
|
|
19849
19851
|
async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
19850
19852
|
const start = Date.now();
|
|
19851
19853
|
const log = defaultLogger;
|
|
19852
|
-
const planJsonPath =
|
|
19853
|
-
const encoderJsonPath =
|
|
19854
|
-
const chunksJsonPath =
|
|
19854
|
+
const planJsonPath = join43(planDir, "plan.json");
|
|
19855
|
+
const encoderJsonPath = join43(planDir, "meta", "encoder.json");
|
|
19856
|
+
const chunksJsonPath = join43(planDir, "meta", "chunks.json");
|
|
19855
19857
|
if (!existsSync29(planJsonPath)) {
|
|
19856
19858
|
throw new RenderChunkValidationError(
|
|
19857
19859
|
MISSING_PLAN_ARTIFACT,
|
|
@@ -19877,7 +19879,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19877
19879
|
}
|
|
19878
19880
|
const encoder = JSON.parse(readFileSync13(encoderJsonPath, "utf-8"));
|
|
19879
19881
|
const chunks = JSON.parse(readFileSync13(chunksJsonPath, "utf-8"));
|
|
19880
|
-
const videosJsonPath =
|
|
19882
|
+
const videosJsonPath = join43(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
19881
19883
|
let planVideos = null;
|
|
19882
19884
|
if (existsSync29(videosJsonPath)) {
|
|
19883
19885
|
try {
|
|
@@ -19909,7 +19911,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19909
19911
|
}
|
|
19910
19912
|
const range = resolveChunkFrameRange(slice, chunkIndex, options?.frameRange);
|
|
19911
19913
|
const framesInChunk = range.endFrame - range.startFrame;
|
|
19912
|
-
const compiledDir =
|
|
19914
|
+
const compiledDir = join43(planDir, "compiled");
|
|
19913
19915
|
if (!existsSync29(compiledDir)) {
|
|
19914
19916
|
throw new RenderChunkValidationError(
|
|
19915
19917
|
MISSING_PLAN_ARTIFACT,
|
|
@@ -19952,7 +19954,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19952
19954
|
crf: encoder.crf,
|
|
19953
19955
|
bitrate: encoder.bitrate,
|
|
19954
19956
|
hdrMode: "force-sdr",
|
|
19955
|
-
entryFile: "index.html"
|
|
19957
|
+
entryFile: "index.html",
|
|
19958
|
+
pageMotionBlur: encoder.pageMotionBlur
|
|
19956
19959
|
});
|
|
19957
19960
|
job.totalFrames = framesInChunk;
|
|
19958
19961
|
job.duration = framesInChunk * plan2.dimensions.fpsDen / plan2.dimensions.fpsNum;
|
|
@@ -19967,7 +19970,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19967
19970
|
};
|
|
19968
19971
|
const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
19969
19972
|
mkdirSync19(workDir, { recursive: true });
|
|
19970
|
-
const framesDir =
|
|
19973
|
+
const framesDir = join43(workDir, "captured-frames");
|
|
19971
19974
|
mkdirSync19(framesDir, { recursive: true });
|
|
19972
19975
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
19973
19976
|
const chunkFrameNumbers = Array.from({ length: framesInChunk }, (_, i) => range.startFrame + i);
|
|
@@ -19992,7 +19995,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
19992
19995
|
dimensions: plan2.dimensions,
|
|
19993
19996
|
encoder,
|
|
19994
19997
|
videoCount: planVideos?.videos.length ?? 0,
|
|
19995
|
-
html: readFileSync13(
|
|
19998
|
+
html: readFileSync13(join43(compiledDir, "index.html"), "utf-8"),
|
|
19996
19999
|
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
19997
20000
|
skipReadinessVideoIds: deferredVideos.filter(
|
|
19998
20001
|
(video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
|
|
@@ -20162,7 +20165,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
20162
20165
|
if (isPngSequence) {
|
|
20163
20166
|
if (!existsSync29(outputChunkPath)) mkdirSync19(outputChunkPath, { recursive: true });
|
|
20164
20167
|
} else {
|
|
20165
|
-
const outDir =
|
|
20168
|
+
const outDir = join43(outputChunkPath, "..");
|
|
20166
20169
|
if (!existsSync29(outDir)) mkdirSync19(outDir, { recursive: true });
|
|
20167
20170
|
}
|
|
20168
20171
|
const encodeInput = {
|
|
@@ -20436,10 +20439,10 @@ async function renderSpan(projectDir, config, span, outputPath) {
|
|
|
20436
20439
|
if (config.cfr === true) {
|
|
20437
20440
|
throw new RenderSpanUnsupportedError("--start/--end cannot be combined with a cfr re-encode.");
|
|
20438
20441
|
}
|
|
20439
|
-
const workDir =
|
|
20442
|
+
const workDir = mkdtempSync(join44(tmpdir4(), "hf-render-span-"));
|
|
20440
20443
|
try {
|
|
20441
|
-
const planDir =
|
|
20442
|
-
const partsDir =
|
|
20444
|
+
const planDir = join44(workDir, "plan");
|
|
20445
|
+
const partsDir = join44(workDir, "parts");
|
|
20443
20446
|
mkdirSync20(partsDir, { recursive: true });
|
|
20444
20447
|
const planned = await plan(
|
|
20445
20448
|
projectDir,
|
|
@@ -20454,7 +20457,7 @@ async function renderSpan(projectDir, config, span, outputPath) {
|
|
|
20454
20457
|
planned.totalFrames
|
|
20455
20458
|
);
|
|
20456
20459
|
const slices = JSON.parse(
|
|
20457
|
-
readFileSync14(
|
|
20460
|
+
readFileSync14(join44(planDir, "meta", "chunks.json"), "utf-8")
|
|
20458
20461
|
);
|
|
20459
20462
|
const parts = [];
|
|
20460
20463
|
for (const slice of slices) {
|
|
@@ -20464,7 +20467,7 @@ async function renderSpan(projectDir, config, span, outputPath) {
|
|
|
20464
20467
|
};
|
|
20465
20468
|
if (range.endFrame <= range.startFrame) continue;
|
|
20466
20469
|
throwIfAborted(config.abortSignal);
|
|
20467
|
-
const part =
|
|
20470
|
+
const part = join44(partsDir, `part-${String(parts.length).padStart(4, "0")}.mp4`);
|
|
20468
20471
|
await renderChunk(planDir, slice.index, part, { frameRange: range });
|
|
20469
20472
|
parts.push(part);
|
|
20470
20473
|
}
|
|
@@ -21083,12 +21086,12 @@ import {
|
|
|
21083
21086
|
existsSync as existsSync32,
|
|
21084
21087
|
mkdirSync as mkdirSync21,
|
|
21085
21088
|
statSync as statSync13,
|
|
21086
|
-
mkdtempSync as
|
|
21089
|
+
mkdtempSync as mkdtempSync2,
|
|
21087
21090
|
writeFileSync as writeFileSync14,
|
|
21088
21091
|
rmSync as rmSync19,
|
|
21089
21092
|
createReadStream as createReadStream5
|
|
21090
21093
|
} from "node:fs";
|
|
21091
|
-
import { resolve as resolve12, dirname as dirname19, join as
|
|
21094
|
+
import { resolve as resolve12, dirname as dirname19, join as join47 } from "node:path";
|
|
21092
21095
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
21093
21096
|
import { parseArgs } from "node:util";
|
|
21094
21097
|
import crypto from "node:crypto";
|
|
@@ -21111,7 +21114,7 @@ import {
|
|
|
21111
21114
|
closeSync as closeSync7,
|
|
21112
21115
|
constants as constants3
|
|
21113
21116
|
} from "node:fs";
|
|
21114
|
-
import { resolve as resolve11, join as
|
|
21117
|
+
import { resolve as resolve11, join as join45, relative as relative7, isAbsolute as isAbsolute5, sep as sep5 } from "node:path";
|
|
21115
21118
|
import { lintHyperframeHtml } from "@hyperframes/lint";
|
|
21116
21119
|
function isStringRecord(value) {
|
|
21117
21120
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -21179,7 +21182,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
21179
21182
|
}
|
|
21180
21183
|
}
|
|
21181
21184
|
return {
|
|
21182
|
-
error: `No HTML entry file found in project directory: ${
|
|
21185
|
+
error: `No HTML entry file found in project directory: ${join45(absProjectDir, preferredEntryFile || "index.html")}`
|
|
21183
21186
|
};
|
|
21184
21187
|
}
|
|
21185
21188
|
function prepareHyperframeLintBody(body) {
|
|
@@ -21225,7 +21228,7 @@ async function runHyperframeLint(prepared) {
|
|
|
21225
21228
|
// src/services/healthWorker.ts
|
|
21226
21229
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
21227
21230
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
21228
|
-
import { dirname as dirname18, join as
|
|
21231
|
+
import { dirname as dirname18, join as join46 } from "node:path";
|
|
21229
21232
|
import { existsSync as existsSync31 } from "node:fs";
|
|
21230
21233
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
21231
21234
|
async function startHealthWorker(options = {}) {
|
|
@@ -21301,7 +21304,7 @@ function defaultLogger2() {
|
|
|
21301
21304
|
}
|
|
21302
21305
|
function resolveWorkerEntry2() {
|
|
21303
21306
|
const here = dirname18(fileURLToPath6(import.meta.url));
|
|
21304
|
-
const candidates = [
|
|
21307
|
+
const candidates = [join46(here, "healthWorkerThread.js"), join46(here, "healthWorkerThread.ts")];
|
|
21305
21308
|
for (const candidate of candidates) {
|
|
21306
21309
|
if (existsSync31(candidate)) return candidate;
|
|
21307
21310
|
}
|
|
@@ -21515,8 +21518,8 @@ async function resolveInlineRenderHtml(body) {
|
|
|
21515
21518
|
}
|
|
21516
21519
|
function materializeInlineProject(html, options) {
|
|
21517
21520
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir5();
|
|
21518
|
-
const tempProjectDir =
|
|
21519
|
-
writeFileSync14(
|
|
21521
|
+
const tempProjectDir = mkdtempSync2(join47(tempRoot, "producer-project-"));
|
|
21522
|
+
writeFileSync14(join47(tempProjectDir, "index.html"), html, "utf-8");
|
|
21520
21523
|
return {
|
|
21521
21524
|
prepared: {
|
|
21522
21525
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -21985,9 +21988,9 @@ import {
|
|
|
21985
21988
|
import { quantizeTimeToFrame, MEDIA_VISUAL_STYLE_PROPERTIES } from "@hyperframes/engine";
|
|
21986
21989
|
|
|
21987
21990
|
// src/services/distributed/planV2Execution.ts
|
|
21988
|
-
import { mkdtempSync as
|
|
21991
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync21 } from "node:fs";
|
|
21989
21992
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
21990
|
-
import { join as
|
|
21993
|
+
import { join as join49 } from "node:path";
|
|
21991
21994
|
|
|
21992
21995
|
// src/services/distributed/assemble.ts
|
|
21993
21996
|
import {
|
|
@@ -21998,14 +22001,13 @@ import {
|
|
|
21998
22001
|
cpSync as cpSync3,
|
|
21999
22002
|
existsSync as existsSync33,
|
|
22000
22003
|
mkdirSync as mkdirSync22,
|
|
22001
|
-
mkdtempSync as mkdtempSync9,
|
|
22002
22004
|
readFileSync as readFileSync16,
|
|
22003
22005
|
readdirSync as readdirSync12,
|
|
22004
22006
|
rmSync as rmSync20,
|
|
22005
22007
|
statSync as statSync14,
|
|
22006
22008
|
writeFileSync as writeFileSync15
|
|
22007
22009
|
} from "node:fs";
|
|
22008
|
-
import { dirname as dirname20, join as
|
|
22010
|
+
import { basename as basename8, dirname as dirname20, join as join48 } from "node:path";
|
|
22009
22011
|
import {
|
|
22010
22012
|
appendRateCapArgs as appendRateCapArgs2,
|
|
22011
22013
|
appendRenderProvenanceArgs as appendRenderProvenanceArgs2,
|
|
@@ -22019,6 +22021,7 @@ import {
|
|
|
22019
22021
|
renameWithRetry as renameWithRetry11
|
|
22020
22022
|
} from "@hyperframes/engine";
|
|
22021
22023
|
import { fpsToFfmpegArg as fpsToFfmpegArg3 } from "@hyperframes/core";
|
|
22024
|
+
import { createStagingDir as createStagingDir7 } from "@hyperframes/core/atomic-file";
|
|
22022
22025
|
function cfrRateControlArgs(locked) {
|
|
22023
22026
|
const preset = getEncoderPreset6(locked.quality ?? "standard", "mp4");
|
|
22024
22027
|
if (locked.crf == null && locked.bitrate) {
|
|
@@ -22054,7 +22057,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
22054
22057
|
if (!existsSync33(dirname20(outputPath))) {
|
|
22055
22058
|
mkdirSync22(dirname20(outputPath), { recursive: true });
|
|
22056
22059
|
}
|
|
22057
|
-
const workDir =
|
|
22060
|
+
const workDir = createStagingDir7(dirname20(outputPath), `${basename8(outputPath)}.assemble-work-`);
|
|
22058
22061
|
const fps = { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen };
|
|
22059
22062
|
const stageMs = {};
|
|
22060
22063
|
const ctx = {
|
|
@@ -22074,7 +22077,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
22074
22077
|
};
|
|
22075
22078
|
try {
|
|
22076
22079
|
const videoSources = cfrEncoder ? [await reencodeCfr(ctx, chunkPaths, cfrEncoder)] : chunkPaths;
|
|
22077
|
-
const finalPath =
|
|
22080
|
+
const finalPath = join48(workDir, `output.${format}`);
|
|
22078
22081
|
const audio = audioPath !== null && existsSync33(audioPath) ? audioPath : null;
|
|
22079
22082
|
await writeJoined(ctx, videoSources, audio, finalPath);
|
|
22080
22083
|
renameWithRetry11(finalPath, outputPath);
|
|
@@ -22103,8 +22106,8 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
22103
22106
|
};
|
|
22104
22107
|
}
|
|
22105
22108
|
function readAssemblePlan(planDir, chunkPaths) {
|
|
22106
|
-
const planJsonPath =
|
|
22107
|
-
const chunksJsonPath =
|
|
22109
|
+
const planJsonPath = join48(planDir, "plan.json");
|
|
22110
|
+
const chunksJsonPath = join48(planDir, "meta", "chunks.json");
|
|
22108
22111
|
if (!existsSync33(planJsonPath)) {
|
|
22109
22112
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
22110
22113
|
}
|
|
@@ -22155,7 +22158,7 @@ function cfrEncodeArgs(inputArgs, encoder, fpsArg) {
|
|
|
22155
22158
|
return args;
|
|
22156
22159
|
}
|
|
22157
22160
|
async function reencodeCfr(ctx, chunkPaths, encoder) {
|
|
22158
|
-
const cfrOutputPath =
|
|
22161
|
+
const cfrOutputPath = join48(ctx.workDir, `cfr.${ctx.format}`);
|
|
22159
22162
|
const cfrArgs = cfrEncodeArgs(videoInputArgs(ctx, chunkPaths), encoder, ctx.fpsArg);
|
|
22160
22163
|
appendRenderProvenanceArgs2(cfrArgs, cfrOutputPath);
|
|
22161
22164
|
cfrArgs.push("-y", cfrOutputPath);
|
|
@@ -22182,7 +22185,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
|
|
|
22182
22185
|
);
|
|
22183
22186
|
return;
|
|
22184
22187
|
}
|
|
22185
|
-
const paddedAudioPath =
|
|
22188
|
+
const paddedAudioPath = join48(ctx.workDir, "audio-padded.m4a");
|
|
22186
22189
|
const padTrimResult = await ctx.timed(
|
|
22187
22190
|
"audioMs",
|
|
22188
22191
|
() => padOrTrimAudioToVideoFrameCount({
|
|
@@ -22222,7 +22225,7 @@ async function writeJoined(ctx, videoSources, audioPath, finalPath) {
|
|
|
22222
22225
|
}
|
|
22223
22226
|
}
|
|
22224
22227
|
function concatDemuxerInput(chunkPaths, workDir, fpsArg) {
|
|
22225
|
-
const listPath =
|
|
22228
|
+
const listPath = join48(workDir, "concat-list.txt");
|
|
22226
22229
|
const body = chunkPaths.map((path) => `file '${path.replace(/'/g, "'\\''")}'`).join("\n");
|
|
22227
22230
|
writeFileSync15(listPath, `${body}
|
|
22228
22231
|
`, "utf-8");
|
|
@@ -22260,11 +22263,11 @@ function stitchesSeparateAudio(planDir) {
|
|
|
22260
22263
|
return lockedAudio(planDir).separateAudio === true;
|
|
22261
22264
|
}
|
|
22262
22265
|
function planFormat(planDir) {
|
|
22263
|
-
const plan2 = JSON.parse(readFileSync16(
|
|
22266
|
+
const plan2 = JSON.parse(readFileSync16(join48(planDir, "plan.json"), "utf-8"));
|
|
22264
22267
|
return plan2.dimensions.format;
|
|
22265
22268
|
}
|
|
22266
22269
|
function lockedAudio(planDir) {
|
|
22267
|
-
const encoderJsonPath =
|
|
22270
|
+
const encoderJsonPath = join48(planDir, "meta", "encoder.json");
|
|
22268
22271
|
if (!existsSync33(encoderJsonPath)) return {};
|
|
22269
22272
|
return JSON.parse(readFileSync16(encoderJsonPath, "utf-8"));
|
|
22270
22273
|
}
|
|
@@ -22274,7 +22277,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
22274
22277
|
`[assemble] cfr=true is only supported for format="mp4" (got "${format}"). Stream-copy paths for webm and mov already produce exact avg_frame_rate; cfr re-encode is not needed.`
|
|
22275
22278
|
);
|
|
22276
22279
|
}
|
|
22277
|
-
const encoderJsonPath =
|
|
22280
|
+
const encoderJsonPath = join48(planDir, "meta", "encoder.json");
|
|
22278
22281
|
if (!existsSync33(encoderJsonPath)) {
|
|
22279
22282
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
22280
22283
|
}
|
|
@@ -22301,8 +22304,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
22301
22304
|
throw new Error(`[assemble] png-sequence chunk has no frames: ${chunkDir}`);
|
|
22302
22305
|
}
|
|
22303
22306
|
for (const frame of frames) {
|
|
22304
|
-
const dst =
|
|
22305
|
-
cpSync3(
|
|
22307
|
+
const dst = join48(outputPath, formatExportFrameName(globalIdx, "png"));
|
|
22308
|
+
cpSync3(join48(chunkDir, frame), dst);
|
|
22306
22309
|
globalIdx += 1;
|
|
22307
22310
|
}
|
|
22308
22311
|
}
|
|
@@ -22312,13 +22315,13 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
22312
22315
|
);
|
|
22313
22316
|
}
|
|
22314
22317
|
if (audioPath !== null && existsSync33(audioPath)) {
|
|
22315
|
-
const sidecar =
|
|
22318
|
+
const sidecar = join48(outputPath, MIXED_AUDIO_FILENAME4);
|
|
22316
22319
|
cpSync3(audioPath, sidecar);
|
|
22317
22320
|
}
|
|
22318
22321
|
let fileSize = 0;
|
|
22319
22322
|
for (const name of readdirSync12(outputPath)) {
|
|
22320
22323
|
try {
|
|
22321
|
-
fileSize += statSync14(
|
|
22324
|
+
fileSize += statSync14(join48(outputPath, name)).size;
|
|
22322
22325
|
} catch {
|
|
22323
22326
|
}
|
|
22324
22327
|
}
|
|
@@ -22332,8 +22335,8 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
22332
22335
|
|
|
22333
22336
|
// src/services/distributed/planV2Execution.ts
|
|
22334
22337
|
async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
22335
|
-
const workRoot =
|
|
22336
|
-
const materializedPlanDir =
|
|
22338
|
+
const workRoot = mkdtempSync3(join49(tmpdir6(), "hf-plan-v2-chunk-"));
|
|
22339
|
+
const materializedPlanDir = join49(workRoot, "plan");
|
|
22337
22340
|
try {
|
|
22338
22341
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
22339
22342
|
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
@@ -22342,8 +22345,8 @@ async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
|
22342
22345
|
}
|
|
22343
22346
|
}
|
|
22344
22347
|
async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
22345
|
-
const workRoot =
|
|
22346
|
-
const materializedPlanDir =
|
|
22348
|
+
const workRoot = mkdtempSync3(join49(tmpdir6(), "hf-plan-v2-assembler-"));
|
|
22349
|
+
const materializedPlanDir = join49(workRoot, "plan");
|
|
22347
22350
|
try {
|
|
22348
22351
|
const materialized = materializePlanV2Target(
|
|
22349
22352
|
planV2Dir,
|