@hyperframes/producer 0.8.225 → 0.8.227
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 +63 -45
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +67 -67
- package/dist/index.js +308 -140
- package/dist/public-server.js +223 -55
- package/dist/services/render/perfSummary.d.ts +5 -1
- package/dist/services/render/stages/extractVideosStage.d.ts +2 -1
- package/dist/services/render/videoLayerPassthroughPlan.d.ts +30 -0
- package/package.json +7 -7
package/dist/distributed.js
CHANGED
|
@@ -68,7 +68,6 @@ import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframe
|
|
|
68
68
|
import {
|
|
69
69
|
existsSync as existsSync10,
|
|
70
70
|
mkdirSync as mkdirSync6,
|
|
71
|
-
readFileSync as readFileSync6,
|
|
72
71
|
readdirSync as readdirSync3,
|
|
73
72
|
rmSync as rmSync5,
|
|
74
73
|
statSync as statSync5,
|
|
@@ -88,6 +87,7 @@ import {
|
|
|
88
87
|
import { HF_AUDIO_GROUP_TAG } from "@hyperframes/core/audio-groups";
|
|
89
88
|
import { extractStandaloneEntryFromIndex, isTemplateEntry } from "@hyperframes/core/compiler";
|
|
90
89
|
import { HTML_BODY_CSS_HEIGHT_FIRST_RE, HTML_BODY_CSS_WIDTH_FIRST_RE } from "@hyperframes/parsers";
|
|
90
|
+
import { readTextFile as readTextFile2 } from "@hyperframes/parsers/asset-resolution";
|
|
91
91
|
import {
|
|
92
92
|
extractMediaMetadata as extractMediaMetadata4,
|
|
93
93
|
resolveConfig as resolveConfig2,
|
|
@@ -1893,7 +1893,7 @@ import { suggestMatchingPreset } from "@hyperframes/core";
|
|
|
1893
1893
|
// src/services/htmlCompiler.ts
|
|
1894
1894
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
1895
1895
|
import { createHash as createHash4 } from "node:crypto";
|
|
1896
|
-
import { createReadStream as createReadStream2, existsSync as existsSync6, mkdirSync as mkdirSync4
|
|
1896
|
+
import { createReadStream as createReadStream2, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
1897
1897
|
import { join as join6, dirname as dirname5, resolve as resolve6, basename as basename3, relative as relative2 } from "path";
|
|
1898
1898
|
import { parseHTML as parseHTML5 } from "linkedom";
|
|
1899
1899
|
import {
|
|
@@ -1946,7 +1946,8 @@ import {
|
|
|
1946
1946
|
} from "@hyperframes/parsers/sub-composition-validity";
|
|
1947
1947
|
import {
|
|
1948
1948
|
isUnresolvedAssetPlaceholder,
|
|
1949
|
-
readProjectFile
|
|
1949
|
+
readProjectFile,
|
|
1950
|
+
readTextFile
|
|
1950
1951
|
} from "@hyperframes/parsers/asset-resolution";
|
|
1951
1952
|
|
|
1952
1953
|
// src/services/renderMediaCollector.ts
|
|
@@ -4035,11 +4036,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
4035
4036
|
return document2.toString();
|
|
4036
4037
|
}
|
|
4037
4038
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
4038
|
-
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
4039
|
-
readFileSync4(htmlPath, "utf-8"),
|
|
4040
|
-
projectDir,
|
|
4041
|
-
htmlPath
|
|
4042
|
-
);
|
|
4039
|
+
const entryHtml = rebaseDirectEntryAssetPaths(readTextFile(htmlPath), projectDir, htmlPath);
|
|
4043
4040
|
const rawHtml = rewriteUnresolvableGsapToCdn(entryHtml, projectDir);
|
|
4044
4041
|
assertSubCompositionsUsable(rawHtml, projectDir);
|
|
4045
4042
|
const probes = /* @__PURE__ */ new Map();
|
|
@@ -4890,7 +4887,7 @@ async function runProbeStage(input) {
|
|
|
4890
4887
|
);
|
|
4891
4888
|
const hasVariableMedia = hasVariableBoundMedia(compiled.html, job.config.variables);
|
|
4892
4889
|
const hasInsertedMedia = hasRuntimeMediaChanges(compiled.html);
|
|
4893
|
-
const needsBrowser = probeRequiresBrowser({
|
|
4890
|
+
const needsBrowser = cfg.videoLayerPassthrough || probeRequiresBrowser({
|
|
4894
4891
|
durationSeconds: composition.duration,
|
|
4895
4892
|
unresolvedCompositionCount: compiled.unresolvedCompositions.length,
|
|
4896
4893
|
hasAutoStart,
|
|
@@ -5522,17 +5519,23 @@ function resolveVideoExtractionPolicy(env = process.env) {
|
|
|
5522
5519
|
const maxTransientRetries = failureMode !== "off" && env.HF_VIDEO_EXTRACTION_MAX_RETRIES?.trim() === "1" ? 1 : 0;
|
|
5523
5520
|
return { failureMode, maxTransientRetries };
|
|
5524
5521
|
}
|
|
5522
|
+
var MAX_NAMED_SOURCES = 5;
|
|
5525
5523
|
var VideoExtractionStageError = class extends Error {
|
|
5526
5524
|
constructor(code, retryable, failures, extractionFailure = buildExtractionFailureMetadata(
|
|
5527
5525
|
failures.map((failure) => ({
|
|
5528
5526
|
kind: failure.kind,
|
|
5529
5527
|
affectedElementCount: failure.count
|
|
5530
5528
|
}))
|
|
5531
|
-
), ffmpegReason) {
|
|
5529
|
+
), ffmpegReason, sources = []) {
|
|
5532
5530
|
const total = failures.reduce((sum2, failure) => sum2 + failure.count, 0);
|
|
5533
5531
|
const breakdown = failures.map((failure) => `${failure.kind}=${failure.count}`).join(",");
|
|
5534
5532
|
const reason = ffmpegReason ? `: ffmpeg said: ${ffmpegReason}` : "";
|
|
5535
|
-
|
|
5533
|
+
const shown = sources.slice(0, MAX_NAMED_SOURCES).join(", ");
|
|
5534
|
+
const more = sources.length > MAX_NAMED_SOURCES ? ` and ${sources.length - MAX_NAMED_SOURCES} more` : "";
|
|
5535
|
+
const files = sources.length > 0 ? ` (${shown}${more})` : "";
|
|
5536
|
+
super(
|
|
5537
|
+
`Video extraction failed for ${total} source(s) [${code}; ${breakdown}]${files}${reason}`
|
|
5538
|
+
);
|
|
5536
5539
|
this.code = code;
|
|
5537
5540
|
this.retryable = retryable;
|
|
5538
5541
|
this.failures = failures;
|
|
@@ -5580,7 +5583,10 @@ function buildVideoExtractionStageError(result) {
|
|
|
5580
5583
|
retryable,
|
|
5581
5584
|
failures,
|
|
5582
5585
|
extractionFailureMetadataFromResult(result),
|
|
5583
|
-
result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason
|
|
5586
|
+
result.errors.find((failure) => failure.ffmpegReason)?.ffmpegReason,
|
|
5587
|
+
[
|
|
5588
|
+
...new Set(result.errors.flatMap((failure) => failure.source ? [failure.source] : []))
|
|
5589
|
+
].sort()
|
|
5584
5590
|
);
|
|
5585
5591
|
}
|
|
5586
5592
|
function buildHdrProbeStageError(failures) {
|
|
@@ -5659,7 +5665,8 @@ async function runExtractVideosStage(input) {
|
|
|
5659
5665
|
assertNotAborted,
|
|
5660
5666
|
materializeSymlinks,
|
|
5661
5667
|
deferRangeExtraction,
|
|
5662
|
-
urlByAbsentLocalPath
|
|
5668
|
+
urlByAbsentLocalPath,
|
|
5669
|
+
skipFrameExtractionIds = []
|
|
5663
5670
|
} = input;
|
|
5664
5671
|
const stage2Start = Date.now();
|
|
5665
5672
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
@@ -5730,7 +5737,10 @@ async function runExtractVideosStage(input) {
|
|
|
5730
5737
|
);
|
|
5731
5738
|
imageColorSpaces.push(...probed);
|
|
5732
5739
|
}
|
|
5733
|
-
if (
|
|
5740
|
+
if (skipFrameExtractionIds.length > 0) {
|
|
5741
|
+
await appendVideoAudioFromProbe({ projectDir, compiledDir, composition, urlByAbsentLocalPath });
|
|
5742
|
+
videoReadinessSkipIds = [...skipFrameExtractionIds];
|
|
5743
|
+
} else if (composition.videos.length > 0) {
|
|
5734
5744
|
const totalVideos = composition.videos.length;
|
|
5735
5745
|
for (let i = 0; i < totalVideos; i++) {
|
|
5736
5746
|
const v = composition.videos[i];
|
|
@@ -5854,6 +5864,14 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
5854
5864
|
);
|
|
5855
5865
|
}
|
|
5856
5866
|
|
|
5867
|
+
// src/services/render/videoLayerPassthroughPlan.ts
|
|
5868
|
+
import {
|
|
5869
|
+
buildVideoLayerInput,
|
|
5870
|
+
isHttpUrl as isHttpUrl3,
|
|
5871
|
+
qualifyVideoLayer,
|
|
5872
|
+
resolveProjectRelativeSrc as resolveProjectRelativeSrc4
|
|
5873
|
+
} from "@hyperframes/engine";
|
|
5874
|
+
|
|
5857
5875
|
// src/services/render/stages/audioStage.ts
|
|
5858
5876
|
import { join as join11 } from "node:path";
|
|
5859
5877
|
import {
|
|
@@ -6856,7 +6874,7 @@ import {
|
|
|
6856
6874
|
existsSync as existsSync9,
|
|
6857
6875
|
mkdirSync as mkdirSync5,
|
|
6858
6876
|
readdirSync as readdirSync2,
|
|
6859
|
-
readFileSync as
|
|
6877
|
+
readFileSync as readFileSync4,
|
|
6860
6878
|
rmSync as rmSync4,
|
|
6861
6879
|
statSync as statSync4,
|
|
6862
6880
|
writeFileSync as writeFileSync3
|
|
@@ -6950,7 +6968,7 @@ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
|
6950
6968
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
6951
6969
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6952
6970
|
}
|
|
6953
|
-
const gif =
|
|
6971
|
+
const gif = readFileSync4(outputPath);
|
|
6954
6972
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
6955
6973
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
6956
6974
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
@@ -6983,7 +7001,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6983
7001
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
6984
7002
|
}
|
|
6985
7003
|
}
|
|
6986
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
7004
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync4(outputPath)) !== false) {
|
|
6987
7005
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
6988
7006
|
if (failure) return failure;
|
|
6989
7007
|
}
|
|
@@ -7654,7 +7672,7 @@ import {
|
|
|
7654
7672
|
// src/services/distributed/shared.ts
|
|
7655
7673
|
import { execFile as execFileCallback } from "node:child_process";
|
|
7656
7674
|
import { dirname as dirname9, join as join15 } from "node:path";
|
|
7657
|
-
import { existsSync as existsSync11, readFileSync as
|
|
7675
|
+
import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
|
|
7658
7676
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7659
7677
|
import { promisify as promisify2 } from "node:util";
|
|
7660
7678
|
import {
|
|
@@ -7933,7 +7951,7 @@ function readProducerVersion() {
|
|
|
7933
7951
|
const candidate = join15(current, "package.json");
|
|
7934
7952
|
if (existsSync11(candidate)) {
|
|
7935
7953
|
try {
|
|
7936
|
-
const pkg = JSON.parse(
|
|
7954
|
+
const pkg = JSON.parse(readFileSync5(candidate, "utf-8"));
|
|
7937
7955
|
if (pkg.name === "@hyperframes/producer" && typeof pkg.version === "string") {
|
|
7938
7956
|
cachedProducerVersion = pkg.version;
|
|
7939
7957
|
return pkg.version;
|
|
@@ -8263,7 +8281,7 @@ function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
|
|
|
8263
8281
|
}
|
|
8264
8282
|
|
|
8265
8283
|
// src/services/render/stages/freezePlan.ts
|
|
8266
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as
|
|
8284
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync6, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8267
8285
|
import { join as join18, relative as relative3, resolve as resolve8 } from "node:path";
|
|
8268
8286
|
|
|
8269
8287
|
// src/services/distributed/planProtocol.ts
|
|
@@ -8503,7 +8521,7 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
8503
8521
|
const assets = [];
|
|
8504
8522
|
for (const file of files) {
|
|
8505
8523
|
if (file.planRelativePath === COMPILED_INDEX_RELATIVE_PATH) {
|
|
8506
|
-
compositionHtml =
|
|
8524
|
+
compositionHtml = readFileSync6(file.absolutePath);
|
|
8507
8525
|
continue;
|
|
8508
8526
|
}
|
|
8509
8527
|
if (HASH_EXCLUDED_PLAN_FILES.has(file.planRelativePath)) continue;
|
|
@@ -8527,7 +8545,7 @@ function writePlanRemoteFiles(planDir, files) {
|
|
|
8527
8545
|
}
|
|
8528
8546
|
function readPlanRemoteFiles(planDir) {
|
|
8529
8547
|
const path = join18(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
8530
|
-
return existsSync13(path) ? JSON.parse(
|
|
8548
|
+
return existsSync13(path) ? JSON.parse(readFileSync6(path, "utf-8")) : [];
|
|
8531
8549
|
}
|
|
8532
8550
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
8533
8551
|
const planJsonPath = join18(planDir, "plan.json");
|
|
@@ -8538,8 +8556,8 @@ function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
|
8538
8556
|
if (!existsSync13(encoderJsonPath)) {
|
|
8539
8557
|
throw new Error(`[freezePlan] meta/encoder.json missing: ${encoderJsonPath}`);
|
|
8540
8558
|
}
|
|
8541
|
-
const planJson = JSON.parse(
|
|
8542
|
-
const encoderConfigCanonicalJson =
|
|
8559
|
+
const planJson = JSON.parse(readFileSync6(planJsonPath, "utf-8"));
|
|
8560
|
+
const encoderConfigCanonicalJson = readFileSync6(encoderJsonPath, "utf-8");
|
|
8543
8561
|
const { compositionHtml, assets } = collectPlanAssetShas(planDir, digests);
|
|
8544
8562
|
return computePlanHash({
|
|
8545
8563
|
compositionHtml,
|
|
@@ -8645,7 +8663,7 @@ function applyRuntimeEnvSnapshot(snapshot, env = process.env) {
|
|
|
8645
8663
|
}
|
|
8646
8664
|
|
|
8647
8665
|
// src/services/distributed/chunkAudio.ts
|
|
8648
|
-
import { closeSync as closeSync4, existsSync as existsSync14, ftruncateSync, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as
|
|
8666
|
+
import { closeSync as closeSync4, existsSync as existsSync14, ftruncateSync, mkdirSync as mkdirSync9, openSync as openSync4, readFileSync as readFileSync7 } from "node:fs";
|
|
8649
8667
|
import { writeFileSync as writeFileSync6, writeSync } from "node:fs";
|
|
8650
8668
|
import { join as join20 } from "node:path";
|
|
8651
8669
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
@@ -9118,7 +9136,7 @@ async function writeChunkAudioSlices(input) {
|
|
|
9118
9136
|
offset += track.sizes[offsets.length - 1];
|
|
9119
9137
|
}
|
|
9120
9138
|
});
|
|
9121
|
-
const bytes =
|
|
9139
|
+
const bytes = readFileSync7(input.audioPath);
|
|
9122
9140
|
const layout = { sizeBytes: bytes.length, payloadStart, payloadEnd, partStarts: {} };
|
|
9123
9141
|
mkdirSync9(join20(input.planDir, SLICE_DIR), { recursive: true });
|
|
9124
9142
|
input.chunks.forEach((_, index) => {
|
|
@@ -9137,8 +9155,8 @@ async function writeChunkAudioSlices(input) {
|
|
|
9137
9155
|
function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
9138
9156
|
const layoutPath = join20(planDir, SLICE_LAYOUT);
|
|
9139
9157
|
if (!existsSync14(layoutPath)) return resolvePlanAudioPath(planDir);
|
|
9140
|
-
const layout = JSON.parse(
|
|
9141
|
-
const frame =
|
|
9158
|
+
const layout = JSON.parse(readFileSync7(layoutPath, "utf-8"));
|
|
9159
|
+
const frame = readFileSync7(join20(planDir, SLICE_FRAME));
|
|
9142
9160
|
const fd = openSync4(scratchPath, "w");
|
|
9143
9161
|
try {
|
|
9144
9162
|
writeSync(fd, frame, 0, layout.payloadStart, 0);
|
|
@@ -9151,7 +9169,7 @@ function chunkAudioSource(planDir, chunkIndex, scratchPath) {
|
|
|
9151
9169
|
);
|
|
9152
9170
|
const start = layout.partStarts[chunkIndex];
|
|
9153
9171
|
if (start !== void 0) {
|
|
9154
|
-
const part =
|
|
9172
|
+
const part = readFileSync7(join20(planDir, chunkAudioSlicePath(chunkIndex)));
|
|
9155
9173
|
writeSync(fd, part, 0, part.length, start);
|
|
9156
9174
|
}
|
|
9157
9175
|
ftruncateSync(fd, layout.sizeBytes);
|
|
@@ -10242,7 +10260,7 @@ import {
|
|
|
10242
10260
|
lstatSync as lstatSync2,
|
|
10243
10261
|
mkdirSync as mkdirSync12,
|
|
10244
10262
|
mkdtempSync as mkdtempSync4,
|
|
10245
|
-
readFileSync as
|
|
10263
|
+
readFileSync as readFileSync8,
|
|
10246
10264
|
readdirSync as readdirSync7,
|
|
10247
10265
|
rmSync as rmSync9,
|
|
10248
10266
|
statSync as statSync7,
|
|
@@ -10391,7 +10409,7 @@ function isNonNegativeIntegerArray(value) {
|
|
|
10391
10409
|
return Array.isArray(value) && value.every((item) => typeof item === "number" && Number.isInteger(item) && item >= 0);
|
|
10392
10410
|
}
|
|
10393
10411
|
function readJsonFile(path, label) {
|
|
10394
|
-
const contents =
|
|
10412
|
+
const contents = readFileSync8(path, "utf-8");
|
|
10395
10413
|
try {
|
|
10396
10414
|
const parsed = JSON.parse(contents);
|
|
10397
10415
|
return parsed;
|
|
@@ -11132,7 +11150,7 @@ import {
|
|
|
11132
11150
|
existsSync as existsSync18,
|
|
11133
11151
|
mkdirSync as mkdirSync13,
|
|
11134
11152
|
mkdtempSync as mkdtempSync5,
|
|
11135
|
-
readFileSync as
|
|
11153
|
+
readFileSync as readFileSync9,
|
|
11136
11154
|
readdirSync as readdirSync8,
|
|
11137
11155
|
rmSync as rmSync10,
|
|
11138
11156
|
statSync as statSync8,
|
|
@@ -11236,13 +11254,13 @@ function readAssemblePlan(planDir, chunkPaths) {
|
|
|
11236
11254
|
if (!existsSync18(planJsonPath)) {
|
|
11237
11255
|
throw new Error(`[assemble] planDir missing plan.json: ${planJsonPath}`);
|
|
11238
11256
|
}
|
|
11239
|
-
const plan2 = JSON.parse(
|
|
11257
|
+
const plan2 = JSON.parse(readFileSync9(planJsonPath, "utf-8"));
|
|
11240
11258
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.assembler);
|
|
11241
11259
|
validatePlanV2MaterializedTarget(planDir, { role: "assembler" });
|
|
11242
11260
|
if (!existsSync18(chunksJsonPath)) {
|
|
11243
11261
|
throw new Error(`[assemble] planDir missing meta/chunks.json: ${chunksJsonPath}`);
|
|
11244
11262
|
}
|
|
11245
|
-
const chunks = JSON.parse(
|
|
11263
|
+
const chunks = JSON.parse(readFileSync9(chunksJsonPath, "utf-8"));
|
|
11246
11264
|
if (chunkPaths.length !== chunks.length) {
|
|
11247
11265
|
throw new Error(
|
|
11248
11266
|
`[assemble] chunkPaths length (${chunkPaths.length}) does not match chunks.json length (${chunks.length}). Adapters must pass one path per chunk, ordered by index.`
|
|
@@ -11388,13 +11406,13 @@ function stitchesSeparateAudio(planDir) {
|
|
|
11388
11406
|
return lockedAudio(planDir).separateAudio === true;
|
|
11389
11407
|
}
|
|
11390
11408
|
function planFormat(planDir) {
|
|
11391
|
-
const plan2 = JSON.parse(
|
|
11409
|
+
const plan2 = JSON.parse(readFileSync9(join26(planDir, "plan.json"), "utf-8"));
|
|
11392
11410
|
return plan2.dimensions.format;
|
|
11393
11411
|
}
|
|
11394
11412
|
function lockedAudio(planDir) {
|
|
11395
11413
|
const encoderJsonPath = join26(planDir, "meta", "encoder.json");
|
|
11396
11414
|
if (!existsSync18(encoderJsonPath)) return {};
|
|
11397
|
-
return JSON.parse(
|
|
11415
|
+
return JSON.parse(readFileSync9(encoderJsonPath, "utf-8"));
|
|
11398
11416
|
}
|
|
11399
11417
|
function readCfrEncoder(planDir, format) {
|
|
11400
11418
|
if (format !== "mp4") {
|
|
@@ -11406,7 +11424,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
11406
11424
|
if (!existsSync18(encoderJsonPath)) {
|
|
11407
11425
|
throw new Error(`[assemble] planDir missing meta/encoder.json: ${encoderJsonPath}`);
|
|
11408
11426
|
}
|
|
11409
|
-
const encoderJson = JSON.parse(
|
|
11427
|
+
const encoderJson = JSON.parse(readFileSync9(encoderJsonPath, "utf-8"));
|
|
11410
11428
|
if (encoderJson.encoder === "libx265-software") {
|
|
11411
11429
|
throw new Error(
|
|
11412
11430
|
`[assemble] cfr=true is not yet supported with codec: "h265". The cfr re-encode pass uses libx264 and would silently transcode the h265 chunks. Either disable cfr or render with codec: "h264".`
|
|
@@ -11460,7 +11478,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
11460
11478
|
|
|
11461
11479
|
// src/services/distributed/renderChunk.ts
|
|
11462
11480
|
import { randomBytes } from "node:crypto";
|
|
11463
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as
|
|
11481
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync10, readdirSync as readdirSync9, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
11464
11482
|
import { cpus } from "node:os";
|
|
11465
11483
|
import { join as join27 } from "node:path";
|
|
11466
11484
|
import {
|
|
@@ -11583,10 +11601,10 @@ async function beginFrameSessionNeedsScreenshotFallback(session, probe = probeBe
|
|
|
11583
11601
|
return !await probe(session.page, timeoutMs, probeTick, session.beginFrameIntervalMs);
|
|
11584
11602
|
}
|
|
11585
11603
|
function hashChunkOutput(outputPath, kind) {
|
|
11586
|
-
if (kind === "file") return sha256Hex(
|
|
11604
|
+
if (kind === "file") return sha256Hex(readFileSync10(outputPath));
|
|
11587
11605
|
const entries = readdirSync9(outputPath).filter((name) => /\.(png|jpg|jpeg)$/i.test(name)).sort();
|
|
11588
11606
|
const lines = entries.map(
|
|
11589
|
-
(name) => `${name}\0${sha256Hex(
|
|
11607
|
+
(name) => `${name}\0${sha256Hex(readFileSync10(join27(outputPath, name)))}`
|
|
11590
11608
|
);
|
|
11591
11609
|
return sha256Hex(lines.join("\0"));
|
|
11592
11610
|
}
|
|
@@ -11612,7 +11630,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11612
11630
|
`[renderChunk] planDir is missing required artifact: ${planJsonPath}`
|
|
11613
11631
|
);
|
|
11614
11632
|
}
|
|
11615
|
-
const plan2 = JSON.parse(
|
|
11633
|
+
const plan2 = JSON.parse(readFileSync10(planJsonPath, "utf-8"));
|
|
11616
11634
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
11617
11635
|
const planHashStarted = Date.now();
|
|
11618
11636
|
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
@@ -11629,13 +11647,13 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11629
11647
|
);
|
|
11630
11648
|
}
|
|
11631
11649
|
}
|
|
11632
|
-
const encoder = JSON.parse(
|
|
11633
|
-
const chunks = JSON.parse(
|
|
11650
|
+
const encoder = JSON.parse(readFileSync10(encoderJsonPath, "utf-8"));
|
|
11651
|
+
const chunks = JSON.parse(readFileSync10(chunksJsonPath, "utf-8"));
|
|
11634
11652
|
const videosJsonPath = join27(planDir, PLAN_VIDEOS_META_RELATIVE_PATH);
|
|
11635
11653
|
let planVideos = null;
|
|
11636
11654
|
if (existsSync19(videosJsonPath)) {
|
|
11637
11655
|
try {
|
|
11638
|
-
planVideos = validatePlanVideosForChunk(JSON.parse(
|
|
11656
|
+
planVideos = validatePlanVideosForChunk(JSON.parse(readFileSync10(videosJsonPath, "utf-8")));
|
|
11639
11657
|
} catch (err) {
|
|
11640
11658
|
if (err instanceof RenderChunkValidationError) throw err;
|
|
11641
11659
|
throw new RenderChunkValidationError(
|
|
@@ -11746,7 +11764,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11746
11764
|
dimensions: plan2.dimensions,
|
|
11747
11765
|
encoder,
|
|
11748
11766
|
videoCount: planVideos?.videos.length ?? 0,
|
|
11749
|
-
html:
|
|
11767
|
+
html: readFileSync10(join27(compiledDir, "index.html"), "utf-8"),
|
|
11750
11768
|
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
11751
11769
|
skipReadinessVideoIds: deferredVideos.filter(
|
|
11752
11770
|
(video) => !shownFrames.get(video.videoId)?.size || remoteVideoIds.has(video.videoId)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "0.1.0",
|
|
3
3
|
"buildId": "dev",
|
|
4
|
-
"sha256": "
|
|
4
|
+
"sha256": "675f38c16c7cbd061ea636f5437c51e18a54e9d89b9da49b105cb34ac9f865a4",
|
|
5
5
|
"artifacts": {
|
|
6
6
|
"iife": "hyperframe.runtime.iife.js",
|
|
7
7
|
"esm": "hyperframe.runtime.mjs"
|