@hyperframes/producer 0.8.206 → 0.8.207
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 +185 -96
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +74 -69
- package/dist/index.js +513 -309
- package/dist/public-server.js +450 -278
- package/dist/renderRequest.d.ts +3 -0
- package/dist/server.d.ts +3 -0
- package/dist/services/distributed/assemble.d.ts +1 -1
- package/dist/services/distributed/plan.d.ts +6 -0
- package/dist/services/render/deliveryEncoder.d.ts +22 -0
- package/dist/services/render/hlsConfig.d.ts +1 -1
- package/dist/services/render/segmentManifest.d.ts +3 -0
- package/dist/services/render/stages/captureHdrStage.d.ts +2 -0
- package/dist/services/render/stages/encodeStage.d.ts +2 -0
- package/dist/services/render/stages/freezePlan.d.ts +3 -0
- package/dist/services/render/stages/probeStage.d.ts +2 -3
- package/dist/services/renderOrchestrator.d.ts +6 -0
- package/package.json +7 -7
package/dist/distributed.js
CHANGED
|
@@ -6,16 +6,17 @@ import {
|
|
|
6
6
|
existsSync as existsSync15,
|
|
7
7
|
linkSync,
|
|
8
8
|
mkdirSync as mkdirSync10,
|
|
9
|
-
rmSync as
|
|
9
|
+
rmSync as rmSync7,
|
|
10
10
|
writeFileSync as writeFileSync7
|
|
11
11
|
} from "node:fs";
|
|
12
12
|
import { extname as extname4, isAbsolute as isAbsolute3, join as join22, relative as relative5, sep as sep2 } from "node:path";
|
|
13
13
|
import {
|
|
14
|
-
fpsToNumber as
|
|
14
|
+
fpsToNumber as fpsToNumber9,
|
|
15
15
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
16
16
|
} from "@hyperframes/core";
|
|
17
17
|
import {
|
|
18
18
|
getEncoderPreset as getEncoderPreset4,
|
|
19
|
+
keyframeIntervalToGopSize as keyframeIntervalToGopSize3,
|
|
19
20
|
normalizeVp9CpuUsed,
|
|
20
21
|
resolveConfig as resolveConfig3,
|
|
21
22
|
declareContentAddressedUrl,
|
|
@@ -69,7 +70,7 @@ import {
|
|
|
69
70
|
mkdirSync as mkdirSync6,
|
|
70
71
|
readFileSync as readFileSync6,
|
|
71
72
|
readdirSync as readdirSync3,
|
|
72
|
-
rmSync as
|
|
73
|
+
rmSync as rmSync5,
|
|
73
74
|
statSync as statSync5,
|
|
74
75
|
writeFileSync as writeFileSync4,
|
|
75
76
|
copyFileSync as copyFileSync5,
|
|
@@ -77,7 +78,7 @@ import {
|
|
|
77
78
|
} from "fs";
|
|
78
79
|
import {
|
|
79
80
|
chainCapture,
|
|
80
|
-
fpsToNumber as
|
|
81
|
+
fpsToNumber as fpsToNumber8,
|
|
81
82
|
HF_COLOR_GRADING_ATTR,
|
|
82
83
|
HF_VFX_ATTR,
|
|
83
84
|
parseVfxChain,
|
|
@@ -822,7 +823,34 @@ function outputNeedsAlpha(format) {
|
|
|
822
823
|
}
|
|
823
824
|
|
|
824
825
|
// src/services/render/hlsConfig.ts
|
|
825
|
-
import {
|
|
826
|
+
import { keyframeIntervalToGopSize } from "@hyperframes/engine";
|
|
827
|
+
|
|
828
|
+
// src/services/render/deliveryEncoder.ts
|
|
829
|
+
import { appendRateCapArgs, keyframeIntervalToGopSize as keyframeIntervalToGopSize2 } from "@hyperframes/engine";
|
|
830
|
+
function validateDeliveryEncoderConfig(config) {
|
|
831
|
+
const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
|
|
832
|
+
if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
|
|
833
|
+
return;
|
|
834
|
+
if (keyframeIntervalSeconds !== void 0) {
|
|
835
|
+
if (!Number.isFinite(keyframeIntervalSeconds) || keyframeIntervalSeconds <= 0) {
|
|
836
|
+
throw new Error(
|
|
837
|
+
`keyframeIntervalSeconds must be a positive number of seconds; got ${keyframeIntervalSeconds}`
|
|
838
|
+
);
|
|
839
|
+
}
|
|
840
|
+
if (format === "hls") {
|
|
841
|
+
throw new Error(
|
|
842
|
+
"keyframeIntervalSeconds does not apply to hls; hlsSegmentSeconds sets its keyframes"
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (format !== void 0 && format !== "mp4") {
|
|
847
|
+
throw new Error(`keyframeIntervalSeconds and maxBitrate apply to mp4 renders, not ${format}`);
|
|
848
|
+
}
|
|
849
|
+
appendRateCapArgs([], { maxBitrate, bufferSize });
|
|
850
|
+
}
|
|
851
|
+
function chunkGopFrames(framesInChunk, intervalFrames) {
|
|
852
|
+
return Math.min(framesInChunk, intervalFrames ?? framesInChunk);
|
|
853
|
+
}
|
|
826
854
|
|
|
827
855
|
// src/services/render/shared.ts
|
|
828
856
|
import {
|
|
@@ -1245,7 +1273,7 @@ import {
|
|
|
1245
1273
|
import { CaptureFailure, classifyCaptureFailure } from "@hyperframes/engine";
|
|
1246
1274
|
|
|
1247
1275
|
// src/services/render/perfSummary.ts
|
|
1248
|
-
import { fpsToNumber as
|
|
1276
|
+
import { fpsToNumber as fpsToNumber2 } from "@hyperframes/core";
|
|
1249
1277
|
function pushWorkerDedupPerfs(results, sink) {
|
|
1250
1278
|
for (const r of results) {
|
|
1251
1279
|
if (r.perf) sink.push(r.perf);
|
|
@@ -1324,7 +1352,7 @@ import {
|
|
|
1324
1352
|
resolveVideoExtractionWindow,
|
|
1325
1353
|
runFfmpeg
|
|
1326
1354
|
} from "@hyperframes/engine";
|
|
1327
|
-
import { fpsToFfmpegArg, fpsToNumber as
|
|
1355
|
+
import { fpsToFfmpegArg, fpsToNumber as fpsToNumber3 } from "@hyperframes/core";
|
|
1328
1356
|
var BYTES_PER_MIB = 1024 * 1024;
|
|
1329
1357
|
var DEFAULT_HDR_WINDOW_BYTES = 512 * BYTES_PER_MIB;
|
|
1330
1358
|
|
|
@@ -1375,7 +1403,7 @@ function resolveVideoCaptureBeyondViewport(videoCount) {
|
|
|
1375
1403
|
}
|
|
1376
1404
|
|
|
1377
1405
|
// src/services/render/captureCost.ts
|
|
1378
|
-
import { fpsToNumber as
|
|
1406
|
+
import { fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
|
|
1379
1407
|
import {
|
|
1380
1408
|
calculateOptimalWorkers,
|
|
1381
1409
|
captureFrameToBuffer as captureFrameToBuffer3,
|
|
@@ -1785,7 +1813,7 @@ async function observeRenderStage(recorder, phase, data, fn, options = {}) {
|
|
|
1785
1813
|
// src/services/render/videoFrameCoverage.ts
|
|
1786
1814
|
import { parseHTML } from "linkedom";
|
|
1787
1815
|
import {
|
|
1788
|
-
fpsToNumber as
|
|
1816
|
+
fpsToNumber as fpsToNumber5,
|
|
1789
1817
|
normalizeRateSpec,
|
|
1790
1818
|
sourceTimeAt,
|
|
1791
1819
|
toFps
|
|
@@ -2993,7 +3021,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
2993
3021
|
}
|
|
2994
3022
|
return false;
|
|
2995
3023
|
}
|
|
2996
|
-
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
3024
|
+
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath, probes) {
|
|
2997
3025
|
let filePath = src;
|
|
2998
3026
|
if (isHttpUrl(src)) {
|
|
2999
3027
|
if (!existsSync6(downloadDir)) mkdirSync4(downloadDir, { recursive: true });
|
|
@@ -3020,45 +3048,52 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
3020
3048
|
if (error instanceof Error && error.stack) wrapped.stack = error.stack;
|
|
3021
3049
|
return wrapped;
|
|
3022
3050
|
};
|
|
3023
|
-
|
|
3024
|
-
let profile;
|
|
3051
|
+
const probe = () => withMediaProbeSlot(async () => {
|
|
3025
3052
|
try {
|
|
3026
3053
|
await assertMediaPayload(filePath, elementIdentity);
|
|
3027
|
-
profile = await probeMediaProfile2(filePath);
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
if (error instanceof NotMediaPayloadError2) {
|
|
3031
|
-
log?.warn(
|
|
3032
|
-
`[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file, so the element is dropped from the render. Point it at a rendered media file.`
|
|
3033
|
-
);
|
|
3034
|
-
}
|
|
3035
|
-
return { duration: null, resolvedPath: filePath };
|
|
3054
|
+
const profile = await probeMediaProfile2(filePath);
|
|
3055
|
+
if (tagName === "video") {
|
|
3056
|
+
return { profile, fileDuration: (await extractMediaMetadata(filePath)).durationSeconds };
|
|
3036
3057
|
}
|
|
3037
|
-
throw withSrcContext(error);
|
|
3038
|
-
}
|
|
3039
|
-
assertAssetMediaTypeProfile(tagName === "video" ? "video" : "audio", profile, elementIdentity);
|
|
3040
|
-
let metadata;
|
|
3041
|
-
if (tagName === "video") {
|
|
3042
3058
|
try {
|
|
3043
|
-
|
|
3044
|
-
} catch (error) {
|
|
3045
|
-
throw withSrcContext(error);
|
|
3046
|
-
}
|
|
3047
|
-
} else {
|
|
3048
|
-
try {
|
|
3049
|
-
metadata = await extractAudioMetadata(filePath);
|
|
3059
|
+
return { profile, fileDuration: (await extractAudioMetadata(filePath)).durationSeconds };
|
|
3050
3060
|
} catch {
|
|
3051
|
-
return {
|
|
3061
|
+
return { profile, fileDuration: null };
|
|
3052
3062
|
}
|
|
3063
|
+
} catch (error) {
|
|
3064
|
+
return { error };
|
|
3053
3065
|
}
|
|
3054
|
-
const fileDuration = metadata.durationSeconds;
|
|
3055
|
-
const duration = resolveNaturalMediaTimelineDurationFromValues(
|
|
3056
|
-
fileDuration,
|
|
3057
|
-
mediaStart,
|
|
3058
|
-
playbackRate
|
|
3059
|
-
);
|
|
3060
|
-
return { duration, resolvedPath: filePath };
|
|
3061
3066
|
});
|
|
3067
|
+
const key = `${tagName}\0${filePath}`;
|
|
3068
|
+
let probed = probes?.get(key);
|
|
3069
|
+
if (!probed) {
|
|
3070
|
+
probed = probe();
|
|
3071
|
+
probes?.set(key, probed);
|
|
3072
|
+
}
|
|
3073
|
+
const outcome = await probed;
|
|
3074
|
+
if ("error" in outcome) {
|
|
3075
|
+
if (tagName !== "video") {
|
|
3076
|
+
if (outcome.error instanceof NotMediaPayloadError2) {
|
|
3077
|
+
log?.warn(
|
|
3078
|
+
`[compile] Audio "${elementIdentity}" (${src}) is a text document, not a media file, so the element is dropped from the render. Point it at a rendered media file.`
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
3081
|
+
return { duration: null, resolvedPath: filePath };
|
|
3082
|
+
}
|
|
3083
|
+
throw withSrcContext(outcome.error);
|
|
3084
|
+
}
|
|
3085
|
+
assertAssetMediaTypeProfile(
|
|
3086
|
+
tagName === "video" ? "video" : "audio",
|
|
3087
|
+
outcome.profile,
|
|
3088
|
+
elementIdentity
|
|
3089
|
+
);
|
|
3090
|
+
if (outcome.fileDuration === null) return { duration: null, resolvedPath: filePath };
|
|
3091
|
+
const duration = resolveNaturalMediaTimelineDurationFromValues(
|
|
3092
|
+
outcome.fileDuration,
|
|
3093
|
+
mediaStart,
|
|
3094
|
+
playbackRate
|
|
3095
|
+
);
|
|
3096
|
+
return { duration, resolvedPath: filePath };
|
|
3062
3097
|
}
|
|
3063
3098
|
function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
3064
3099
|
const unresolvedIds = new Set(unresolvedMedia.map((element) => element.id));
|
|
@@ -3072,7 +3107,7 @@ function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
|
3072
3107
|
}
|
|
3073
3108
|
return changed ? document2.toString() : html;
|
|
3074
3109
|
}
|
|
3075
|
-
async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocalPath) {
|
|
3110
|
+
async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocalPath, probes) {
|
|
3076
3111
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
|
|
3077
3112
|
const mediaUnresolved = unresolved.filter(
|
|
3078
3113
|
(el) => (el.tagName === "video" || el.tagName === "audio") && el.src
|
|
@@ -3089,7 +3124,8 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
|
|
|
3089
3124
|
el.tagName,
|
|
3090
3125
|
el.id,
|
|
3091
3126
|
log,
|
|
3092
|
-
urlByAbsentLocalPath
|
|
3127
|
+
urlByAbsentLocalPath,
|
|
3128
|
+
probes
|
|
3093
3129
|
).then(({ duration }) => ({ id: el.id, duration }))
|
|
3094
3130
|
)
|
|
3095
3131
|
);
|
|
@@ -3110,7 +3146,9 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
|
|
|
3110
3146
|
downloadDir,
|
|
3111
3147
|
el.tagName,
|
|
3112
3148
|
el.id,
|
|
3113
|
-
log
|
|
3149
|
+
log,
|
|
3150
|
+
void 0,
|
|
3151
|
+
probes
|
|
3114
3152
|
);
|
|
3115
3153
|
return { id: el.id, tagName: el.tagName, duration: el.duration, maxDuration, src: el.src };
|
|
3116
3154
|
})
|
|
@@ -3132,7 +3170,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
|
|
|
3132
3170
|
compiledHtml = compiledHtml.replace(/(<audio\b[^>]*)\s+crossorigin(?:=["'][^"']*["'])?/gi, "$1");
|
|
3133
3171
|
return { html: compiledHtml, unresolvedCompositions };
|
|
3134
3172
|
}
|
|
3135
|
-
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
3173
|
+
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath, probes) {
|
|
3136
3174
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
3137
3175
|
const { document: document2 } = parseHTML5(html);
|
|
3138
3176
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
@@ -3160,14 +3198,16 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
3160
3198
|
dirname5(item.filePath),
|
|
3161
3199
|
downloadDir,
|
|
3162
3200
|
void 0,
|
|
3163
|
-
urlByAbsentLocalPath
|
|
3201
|
+
urlByAbsentLocalPath,
|
|
3202
|
+
probes
|
|
3164
3203
|
);
|
|
3165
3204
|
const nested = await parseSubCompositions(
|
|
3166
3205
|
compiledSub,
|
|
3167
3206
|
projectDir,
|
|
3168
3207
|
downloadDir,
|
|
3169
3208
|
item.nestedVisited,
|
|
3170
|
-
urlByAbsentLocalPath
|
|
3209
|
+
urlByAbsentLocalPath,
|
|
3210
|
+
probes
|
|
3171
3211
|
);
|
|
3172
3212
|
return {
|
|
3173
3213
|
srcPath: item.srcPath,
|
|
@@ -3937,19 +3977,22 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
3937
3977
|
);
|
|
3938
3978
|
const rawHtml = rewriteUnresolvableGsapToCdn(entryHtml, projectDir);
|
|
3939
3979
|
assertSubCompositionsUsable(rawHtml, projectDir);
|
|
3980
|
+
const probes = /* @__PURE__ */ new Map();
|
|
3940
3981
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
3941
3982
|
rawHtml,
|
|
3942
3983
|
projectDir,
|
|
3943
3984
|
downloadDir,
|
|
3944
3985
|
options.log,
|
|
3945
|
-
options.urlByAbsentLocalPath
|
|
3986
|
+
options.urlByAbsentLocalPath,
|
|
3987
|
+
probes
|
|
3946
3988
|
);
|
|
3947
3989
|
const { subCompositions } = await parseSubCompositions(
|
|
3948
3990
|
compiledHtml,
|
|
3949
3991
|
projectDir,
|
|
3950
3992
|
downloadDir,
|
|
3951
3993
|
void 0,
|
|
3952
|
-
options.urlByAbsentLocalPath
|
|
3994
|
+
options.urlByAbsentLocalPath,
|
|
3995
|
+
probes
|
|
3953
3996
|
);
|
|
3954
3997
|
const fullHtml = ensureFullDocument(compiledHtml);
|
|
3955
3998
|
const inlinedHtml = inlineSubCompositions(
|
|
@@ -4052,9 +4095,12 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
4052
4095
|
}
|
|
4053
4096
|
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
|
4054
4097
|
const masterFxChain = parseMasterFxChain(html);
|
|
4098
|
+
const advised = /* @__PURE__ */ new Set();
|
|
4055
4099
|
for (const video of videos) {
|
|
4056
4100
|
if (isHttpUrl(video.src)) continue;
|
|
4057
4101
|
const videoPath = resolve6(projectDir, video.src);
|
|
4102
|
+
if (advised.has(videoPath)) continue;
|
|
4103
|
+
advised.add(videoPath);
|
|
4058
4104
|
const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
|
|
4059
4105
|
(async () => {
|
|
4060
4106
|
const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
|
|
@@ -4605,13 +4651,14 @@ import {
|
|
|
4605
4651
|
} from "@hyperframes/engine";
|
|
4606
4652
|
import {
|
|
4607
4653
|
MEDIA_RENDER_ID_ATTR as MEDIA_RENDER_ID_ATTR2,
|
|
4608
|
-
fpsToNumber as
|
|
4654
|
+
fpsToNumber as fpsToNumber6,
|
|
4609
4655
|
normalizeRateSpec as normalizeRateSpec3,
|
|
4610
4656
|
readMediaStart as readMediaStart2,
|
|
4611
4657
|
shiftRateLane as shiftRateLane2,
|
|
4612
4658
|
sourceTimeAt as sourceTimeAt3,
|
|
4613
4659
|
timeAtSourceTime
|
|
4614
4660
|
} from "@hyperframes/core";
|
|
4661
|
+
import { parseHostVariableValues as parseHostVariableValues2, readDeclaredDefaults as readDeclaredDefaults2 } from "@hyperframes/core/compiler";
|
|
4615
4662
|
import { extractMediaSrcMutations } from "@hyperframes/parsers";
|
|
4616
4663
|
|
|
4617
4664
|
// src/services/render/stages/probeFailures.ts
|
|
@@ -4649,8 +4696,15 @@ function hasAutoStartVideos(html) {
|
|
|
4649
4696
|
const { document: document2 } = parseHTML6(html);
|
|
4650
4697
|
return document2.querySelector("video[data-hf-auto-start]") !== null;
|
|
4651
4698
|
}
|
|
4699
|
+
function variableHasValue(element, variableId, variables) {
|
|
4700
|
+
if (variables && Object.hasOwn(variables, variableId)) return true;
|
|
4701
|
+
for (let scope = element.parentElement; scope; scope = scope.parentElement) {
|
|
4702
|
+
if (Object.hasOwn(parseHostVariableValues2(scope), variableId)) return true;
|
|
4703
|
+
if (Object.hasOwn(readDeclaredDefaults2(scope), variableId)) return true;
|
|
4704
|
+
}
|
|
4705
|
+
return Object.hasOwn(readDeclaredDefaults2(element.ownerDocument.documentElement), variableId);
|
|
4706
|
+
}
|
|
4652
4707
|
function hasVariableBoundMedia(html, variables) {
|
|
4653
|
-
if (!variables || Object.keys(variables).length === 0) return false;
|
|
4654
4708
|
const { document: document2 } = parseHTML6(html);
|
|
4655
4709
|
return Array.from(
|
|
4656
4710
|
document2.querySelectorAll(
|
|
@@ -4658,7 +4712,7 @@ function hasVariableBoundMedia(html, variables) {
|
|
|
4658
4712
|
)
|
|
4659
4713
|
).some((element) => {
|
|
4660
4714
|
const variableId = element.getAttribute("data-var-src")?.trim();
|
|
4661
|
-
return Boolean(variableId &&
|
|
4715
|
+
return Boolean(variableId && variableHasValue(element, variableId, variables));
|
|
4662
4716
|
});
|
|
4663
4717
|
}
|
|
4664
4718
|
function authoredMediaStarts(html) {
|
|
@@ -5084,7 +5138,7 @@ async function runProbeStage(input) {
|
|
|
5084
5138
|
session.page,
|
|
5085
5139
|
composition.audios.map((audio) => audio.id),
|
|
5086
5140
|
composition.duration,
|
|
5087
|
-
|
|
5141
|
+
fpsToNumber6(job.config.fps)
|
|
5088
5142
|
);
|
|
5089
5143
|
assertNotAborted();
|
|
5090
5144
|
if (automation.length > 0) {
|
|
@@ -5147,7 +5201,7 @@ async function runProbeStage(input) {
|
|
|
5147
5201
|
}
|
|
5148
5202
|
const browserProbeMs = Date.now() - probeStart;
|
|
5149
5203
|
const duration = composition.duration;
|
|
5150
|
-
const totalFrames = durationToFrameCount(duration,
|
|
5204
|
+
const totalFrames = durationToFrameCount(duration, fpsToNumber6(job.config.fps));
|
|
5151
5205
|
if (duration <= 0) {
|
|
5152
5206
|
const diagnostics = [];
|
|
5153
5207
|
try {
|
|
@@ -5710,16 +5764,17 @@ async function appendVideoAudioFromProbe(input) {
|
|
|
5710
5764
|
|
|
5711
5765
|
// src/services/render/stages/audioStage.ts
|
|
5712
5766
|
import { join as join11 } from "node:path";
|
|
5713
|
-
import { rmSync as rmSync5 } from "node:fs";
|
|
5714
5767
|
import {
|
|
5715
5768
|
MIXED_AUDIO_FILENAME,
|
|
5716
5769
|
audioFailureDiagnostics,
|
|
5717
|
-
processCompositionAudio
|
|
5770
|
+
processCompositionAudio,
|
|
5771
|
+
removeScratch as removeScratch2,
|
|
5772
|
+
collectScratchLeftovers
|
|
5718
5773
|
} from "@hyperframes/engine";
|
|
5719
5774
|
|
|
5720
5775
|
// src/services/render/audioPadTrim.ts
|
|
5721
5776
|
import { spawn } from "node:child_process";
|
|
5722
|
-
import { mkdtempSync
|
|
5777
|
+
import { mkdtempSync } from "node:fs";
|
|
5723
5778
|
import { dirname as dirname6, join as join10 } from "node:path";
|
|
5724
5779
|
import {
|
|
5725
5780
|
DEFAULT_CONFIG,
|
|
@@ -5728,6 +5783,7 @@ import {
|
|
|
5728
5783
|
extractAudioMetadata as extractAudioMetadata3,
|
|
5729
5784
|
getFfprobeBinary,
|
|
5730
5785
|
ManagedChildProcess,
|
|
5786
|
+
removeScratch,
|
|
5731
5787
|
renameWithRetry as renameWithRetry5,
|
|
5732
5788
|
runFfmpeg as runFfmpeg5,
|
|
5733
5789
|
trackChildProcess
|
|
@@ -5923,7 +5979,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
5923
5979
|
error: `audioPadTrim: failed to materialize ${plan2.operation}: ${err instanceof Error ? err.message : String(err)}`
|
|
5924
5980
|
};
|
|
5925
5981
|
} finally {
|
|
5926
|
-
for (const path of plan2.cleanupPaths)
|
|
5982
|
+
for (const path of plan2.cleanupPaths) removeScratch(path);
|
|
5927
5983
|
}
|
|
5928
5984
|
let loweredDb = 0;
|
|
5929
5985
|
if (probeTruePeak) {
|
|
@@ -5986,7 +6042,7 @@ async function enforceAacTruePeak(input) {
|
|
|
5986
6042
|
}
|
|
5987
6043
|
if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
|
|
5988
6044
|
if (measuredPath !== correctedPath) return { success: true };
|
|
5989
|
-
renameWithRetry5(correctedPath, input.audioPath);
|
|
6045
|
+
renameWithRetry5(correctedPath, input.audioPath, { copyFallback: true });
|
|
5990
6046
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
5991
6047
|
}
|
|
5992
6048
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
@@ -6027,7 +6083,7 @@ async function enforceAacTruePeak(input) {
|
|
|
6027
6083
|
)}`
|
|
6028
6084
|
};
|
|
6029
6085
|
} finally {
|
|
6030
|
-
if (scratchDir)
|
|
6086
|
+
if (scratchDir) removeScratch(scratchDir);
|
|
6031
6087
|
}
|
|
6032
6088
|
}
|
|
6033
6089
|
function failResult(outputPath, target, source, error) {
|
|
@@ -6172,7 +6228,15 @@ function assertAudioNotInterrupted(failures) {
|
|
|
6172
6228
|
])
|
|
6173
6229
|
});
|
|
6174
6230
|
}
|
|
6175
|
-
|
|
6231
|
+
function runAudioStage(input) {
|
|
6232
|
+
return collectScratchLeftovers(
|
|
6233
|
+
() => runAudioStageScoped(input),
|
|
6234
|
+
(left) => {
|
|
6235
|
+
if (left.length > 0) input.log?.warn("[audio] scratch left behind", { count: left.length });
|
|
6236
|
+
}
|
|
6237
|
+
);
|
|
6238
|
+
}
|
|
6239
|
+
async function runAudioStageScoped(input) {
|
|
6176
6240
|
const stageStart = Date.now();
|
|
6177
6241
|
const pcmSourcePath = join11(input.workDir, "audio-source.wav");
|
|
6178
6242
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
@@ -6194,7 +6258,7 @@ async function runAudioStage(input) {
|
|
|
6194
6258
|
}
|
|
6195
6259
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
6196
6260
|
} finally {
|
|
6197
|
-
|
|
6261
|
+
removeScratch2(pcmSourcePath);
|
|
6198
6262
|
}
|
|
6199
6263
|
}
|
|
6200
6264
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
@@ -6653,7 +6717,7 @@ import {
|
|
|
6653
6717
|
initializeSession as initializeSession7,
|
|
6654
6718
|
spawnStreamingEncoder as spawnStreamingEncoder3
|
|
6655
6719
|
} from "@hyperframes/engine";
|
|
6656
|
-
import { fpsToNumber as
|
|
6720
|
+
import { fpsToNumber as fpsToNumber7 } from "@hyperframes/core";
|
|
6657
6721
|
|
|
6658
6722
|
// src/services/hdrImageTransferCache.ts
|
|
6659
6723
|
import { convertTransfer as convertTransfer2 } from "@hyperframes/engine";
|
|
@@ -6683,7 +6747,7 @@ import {
|
|
|
6683
6747
|
mkdirSync as mkdirSync5,
|
|
6684
6748
|
readdirSync as readdirSync2,
|
|
6685
6749
|
readFileSync as readFileSync5,
|
|
6686
|
-
rmSync as
|
|
6750
|
+
rmSync as rmSync4,
|
|
6687
6751
|
statSync as statSync4,
|
|
6688
6752
|
writeFileSync as writeFileSync3
|
|
6689
6753
|
} from "node:fs";
|
|
@@ -6822,7 +6886,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
6822
6886
|
fileSize
|
|
6823
6887
|
};
|
|
6824
6888
|
} finally {
|
|
6825
|
-
|
|
6889
|
+
rmSync4(input.palettePath, { force: true });
|
|
6826
6890
|
}
|
|
6827
6891
|
}
|
|
6828
6892
|
function startEncodeProgress(job, stage, onProgress) {
|
|
@@ -6846,6 +6910,8 @@ function videoEncoderOptions(input, engineCfg) {
|
|
|
6846
6910
|
preset: preset.preset,
|
|
6847
6911
|
quality: input.effectiveQuality,
|
|
6848
6912
|
bitrate: input.effectiveBitrate,
|
|
6913
|
+
maxBitrate: input.maxBitrate,
|
|
6914
|
+
bufferSize: input.bufferSize,
|
|
6849
6915
|
pixelFormat: preset.pixelFormat,
|
|
6850
6916
|
vp9CpuUsed: engineCfg.vp9CpuUsed,
|
|
6851
6917
|
useGpu: job.config.useGpu,
|
|
@@ -8468,7 +8534,7 @@ import { join as join20 } from "node:path";
|
|
|
8468
8534
|
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
8469
8535
|
|
|
8470
8536
|
// src/services/distributed/mp4Stitch.ts
|
|
8471
|
-
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, rmSync as
|
|
8537
|
+
import { createReadStream as createReadStream4, createWriteStream, mkdirSync as mkdirSync8, mkdtempSync as mkdtempSync2, rmSync as rmSync6 } from "node:fs";
|
|
8472
8538
|
import { open } from "node:fs/promises";
|
|
8473
8539
|
import { renameWithRetry as renameWithRetry6 } from "@hyperframes/engine";
|
|
8474
8540
|
import { dirname as dirname10, join as join19 } from "node:path";
|
|
@@ -8846,7 +8912,7 @@ async function writeStitchedMp4(chunkPaths, outputPath, audioPath) {
|
|
|
8846
8912
|
renameWithRetry6(join19(workDir, "out.mp4"), outputPath);
|
|
8847
8913
|
} finally {
|
|
8848
8914
|
out.destroy();
|
|
8849
|
-
|
|
8915
|
+
rmSync6(workDir, { recursive: true, force: true });
|
|
8850
8916
|
}
|
|
8851
8917
|
}
|
|
8852
8918
|
|
|
@@ -9358,7 +9424,7 @@ var FREEZE_OWNED_PLAN_FILES = [
|
|
|
9358
9424
|
];
|
|
9359
9425
|
function removeFreezeOwnedPlanFiles(planDir) {
|
|
9360
9426
|
for (const relativePath of FREEZE_OWNED_PLAN_FILES) {
|
|
9361
|
-
|
|
9427
|
+
rmSync7(join22(planDir, relativePath), { force: true });
|
|
9362
9428
|
}
|
|
9363
9429
|
}
|
|
9364
9430
|
function buildChunkSlices(totalFrames, chunkCount, effectiveChunkSize) {
|
|
@@ -9418,6 +9484,9 @@ function buildLockedRenderConfig(input) {
|
|
|
9418
9484
|
preset,
|
|
9419
9485
|
crf: config.crf,
|
|
9420
9486
|
bitrate: config.bitrate,
|
|
9487
|
+
maxBitrate: config.maxBitrate,
|
|
9488
|
+
bufferSize: config.bufferSize,
|
|
9489
|
+
keyframeIntervalFrames: config.keyframeIntervalSeconds === void 0 ? void 0 : keyframeIntervalToGopSize3({ num: config.fps, den: 1 }, config.keyframeIntervalSeconds),
|
|
9421
9490
|
...locksVp9CpuUsed,
|
|
9422
9491
|
// GOP === chunkSize so every chunk's first frame is an IDR keyframe and
|
|
9423
9492
|
// ffmpeg concat-copy round-trips losslessly.
|
|
@@ -9569,7 +9638,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
9569
9638
|
throw new Error(`[plan] entry file not found: ${htmlPath}`);
|
|
9570
9639
|
}
|
|
9571
9640
|
const workDir = join22(planDir, ".plan-work");
|
|
9572
|
-
|
|
9641
|
+
rmSync7(workDir, { recursive: true, force: true });
|
|
9573
9642
|
mkdirSync10(workDir, { recursive: true });
|
|
9574
9643
|
const compiledDir = join22(workDir, "compiled");
|
|
9575
9644
|
mkdirSync10(compiledDir, { recursive: true });
|
|
@@ -9662,7 +9731,7 @@ async function compilePlanComposition(projectDir, config, planDir, options) {
|
|
|
9662
9731
|
validateDistributedDuration({
|
|
9663
9732
|
duration: probeResult.duration,
|
|
9664
9733
|
totalFrames,
|
|
9665
|
-
fps:
|
|
9734
|
+
fps: fpsToNumber9(job.config.fps)
|
|
9666
9735
|
});
|
|
9667
9736
|
if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log);
|
|
9668
9737
|
if (probeResult.probeSession) {
|
|
@@ -9776,15 +9845,15 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9776
9845
|
logPlanStage(log, "extract", stageStarted);
|
|
9777
9846
|
const stagedVideoFrames = join22(compiledDir, "__hyperframes_video_frames");
|
|
9778
9847
|
const videoFramesDst = join22(planDir, "video-frames");
|
|
9779
|
-
if (existsSync15(videoFramesDst))
|
|
9848
|
+
if (existsSync15(videoFramesDst)) rmSync7(videoFramesDst, { recursive: true, force: true });
|
|
9780
9849
|
const videoSourcesDir = join22(planDir, "video-sources");
|
|
9781
|
-
|
|
9850
|
+
rmSync7(videoSourcesDir, { recursive: true, force: true });
|
|
9782
9851
|
if (existsSync15(stagedVideoFrames)) {
|
|
9783
9852
|
renameWithRetry7(stagedVideoFrames, videoFramesDst);
|
|
9784
9853
|
} else {
|
|
9785
9854
|
mkdirSync10(videoFramesDst, { recursive: true });
|
|
9786
9855
|
}
|
|
9787
|
-
if (existsSync15(finalCompiledDir))
|
|
9856
|
+
if (existsSync15(finalCompiledDir)) rmSync7(finalCompiledDir, { recursive: true, force: true });
|
|
9788
9857
|
renameWithRetry7(compiledDir, finalCompiledDir);
|
|
9789
9858
|
const shipVideoSource = createVideoSourceShipper({
|
|
9790
9859
|
planDir,
|
|
@@ -9952,7 +10021,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
9952
10021
|
format: config.format
|
|
9953
10022
|
};
|
|
9954
10023
|
try {
|
|
9955
|
-
|
|
10024
|
+
rmSync7(workDir, { recursive: true, force: true });
|
|
9956
10025
|
} catch (err) {
|
|
9957
10026
|
log.warn("[plan] failed to remove temp work dir", {
|
|
9958
10027
|
workDir,
|
|
@@ -10022,7 +10091,7 @@ async function buildPlanAudio(projectDir, config, workRoot, outputPath, options
|
|
|
10022
10091
|
renameWithRetry7(mixed.audioOutputPath, outputPath);
|
|
10023
10092
|
return { hasAudio: true };
|
|
10024
10093
|
} finally {
|
|
10025
|
-
|
|
10094
|
+
rmSync7(c.workDir, { recursive: true, force: true });
|
|
10026
10095
|
}
|
|
10027
10096
|
}
|
|
10028
10097
|
async function plan(projectDir, config, planDir, options = {}) {
|
|
@@ -10051,7 +10120,7 @@ import {
|
|
|
10051
10120
|
mkdtempSync as mkdtempSync4,
|
|
10052
10121
|
readFileSync as readFileSync10,
|
|
10053
10122
|
readdirSync as readdirSync7,
|
|
10054
|
-
rmSync as
|
|
10123
|
+
rmSync as rmSync9,
|
|
10055
10124
|
statSync as statSync7,
|
|
10056
10125
|
writeFileSync as writeFileSync9
|
|
10057
10126
|
} from "node:fs";
|
|
@@ -10067,7 +10136,7 @@ import {
|
|
|
10067
10136
|
linkSync as linkSync2,
|
|
10068
10137
|
mkdirSync as mkdirSync11,
|
|
10069
10138
|
mkdtempSync as mkdtempSync3,
|
|
10070
|
-
rmSync as
|
|
10139
|
+
rmSync as rmSync8,
|
|
10071
10140
|
statSync as statSync6,
|
|
10072
10141
|
writeFileSync as writeFileSync8
|
|
10073
10142
|
} from "node:fs";
|
|
@@ -10162,7 +10231,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
10162
10231
|
}
|
|
10163
10232
|
renameWithRetry8(temporaryPath, destinationPath);
|
|
10164
10233
|
} finally {
|
|
10165
|
-
|
|
10234
|
+
rmSync8(stagingDir, { recursive: true, force: true });
|
|
10166
10235
|
}
|
|
10167
10236
|
}
|
|
10168
10237
|
async commitManifest(manifestBytes) {
|
|
@@ -10179,7 +10248,7 @@ var LocalPlanV2ArtifactPublisher = class {
|
|
|
10179
10248
|
}
|
|
10180
10249
|
async abort() {
|
|
10181
10250
|
if (!this.#committed) {
|
|
10182
|
-
|
|
10251
|
+
rmSync8(this.temporaryDir, { recursive: true, force: true });
|
|
10183
10252
|
}
|
|
10184
10253
|
}
|
|
10185
10254
|
};
|
|
@@ -10448,7 +10517,7 @@ function writeBlob(sourcePath, destinationPath) {
|
|
|
10448
10517
|
copyFileSync8(sourcePath, temporaryPath);
|
|
10449
10518
|
renameWithRetry9(temporaryPath, destinationPath);
|
|
10450
10519
|
} finally {
|
|
10451
|
-
|
|
10520
|
+
rmSync9(temporaryDir, { recursive: true, force: true });
|
|
10452
10521
|
}
|
|
10453
10522
|
}
|
|
10454
10523
|
function publishBlobFor(absolutePath, digests) {
|
|
@@ -10581,7 +10650,7 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
10581
10650
|
renameWithRetry9(tempDir, planV2Dir);
|
|
10582
10651
|
return resultFromManifest(planV2Dir, publication.manifest);
|
|
10583
10652
|
} catch (error) {
|
|
10584
|
-
|
|
10653
|
+
rmSync9(tempDir, { recursive: true, force: true });
|
|
10585
10654
|
throw error;
|
|
10586
10655
|
}
|
|
10587
10656
|
}
|
|
@@ -10648,7 +10717,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
10648
10717
|
}
|
|
10649
10718
|
throw error;
|
|
10650
10719
|
} finally {
|
|
10651
|
-
|
|
10720
|
+
rmSync9(stagingRoot, { recursive: true, force: true });
|
|
10652
10721
|
}
|
|
10653
10722
|
}
|
|
10654
10723
|
async function planV2(projectDir, config, planV2Dir) {
|
|
@@ -10865,7 +10934,7 @@ function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}
|
|
|
10865
10934
|
);
|
|
10866
10935
|
renameWithRetry9(tempDir, destinationDir);
|
|
10867
10936
|
} catch (error) {
|
|
10868
|
-
|
|
10937
|
+
rmSync9(tempDir, { recursive: true, force: true });
|
|
10869
10938
|
throw error;
|
|
10870
10939
|
}
|
|
10871
10940
|
return {
|
|
@@ -10929,7 +10998,7 @@ function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
|
10929
10998
|
}
|
|
10930
10999
|
|
|
10931
11000
|
// src/services/distributed/planV2Execution.ts
|
|
10932
|
-
import { mkdtempSync as mkdtempSync6, rmSync as
|
|
11001
|
+
import { mkdtempSync as mkdtempSync6, rmSync as rmSync12 } from "node:fs";
|
|
10933
11002
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
10934
11003
|
import { join as join28 } from "node:path";
|
|
10935
11004
|
|
|
@@ -10941,12 +11010,13 @@ import {
|
|
|
10941
11010
|
mkdtempSync as mkdtempSync5,
|
|
10942
11011
|
readFileSync as readFileSync11,
|
|
10943
11012
|
readdirSync as readdirSync8,
|
|
10944
|
-
rmSync as
|
|
11013
|
+
rmSync as rmSync10,
|
|
10945
11014
|
statSync as statSync8,
|
|
10946
11015
|
writeFileSync as writeFileSync10
|
|
10947
11016
|
} from "node:fs";
|
|
10948
11017
|
import { dirname as dirname13, join as join26 } from "node:path";
|
|
10949
11018
|
import {
|
|
11019
|
+
appendRateCapArgs as appendRateCapArgs2,
|
|
10950
11020
|
appendRenderProvenanceArgs as appendRenderProvenanceArgs2,
|
|
10951
11021
|
describeFfmpegFailure as describeFfmpegFailure4,
|
|
10952
11022
|
getEncoderPreset as getEncoderPreset5,
|
|
@@ -11020,7 +11090,7 @@ async function assemble(planDir, chunkOutputs, audioPath, outputPath, options) {
|
|
|
11020
11090
|
});
|
|
11021
11091
|
} finally {
|
|
11022
11092
|
try {
|
|
11023
|
-
|
|
11093
|
+
rmSync10(workDir, { recursive: true, force: true });
|
|
11024
11094
|
} catch (err) {
|
|
11025
11095
|
log.warn("[assemble] failed to remove work dir", {
|
|
11026
11096
|
workDir,
|
|
@@ -11082,6 +11152,9 @@ function cfrEncodeArgs(inputArgs, encoder, fpsArg) {
|
|
|
11082
11152
|
"-r",
|
|
11083
11153
|
fpsArg
|
|
11084
11154
|
];
|
|
11155
|
+
if (encoder.keyframeIntervalFrames !== void 0)
|
|
11156
|
+
args.push("-g", String(encoder.keyframeIntervalFrames));
|
|
11157
|
+
appendRateCapArgs2(args, encoder);
|
|
11085
11158
|
appendSoftwareEncoderThreadsArg(args, "libx264");
|
|
11086
11159
|
return args;
|
|
11087
11160
|
}
|
|
@@ -11218,7 +11291,7 @@ function readCfrEncoder(planDir, format) {
|
|
|
11218
11291
|
return encoderJson;
|
|
11219
11292
|
}
|
|
11220
11293
|
function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, startTimeMs) {
|
|
11221
|
-
if (existsSync18(outputPath))
|
|
11294
|
+
if (existsSync18(outputPath)) rmSync10(outputPath, { recursive: true, force: true });
|
|
11222
11295
|
mkdirSync13(outputPath, { recursive: true });
|
|
11223
11296
|
let globalIdx = 0;
|
|
11224
11297
|
for (const chunkDir of chunkPaths) {
|
|
@@ -11263,7 +11336,7 @@ function mergePngFrameDirs(chunkPaths, outputPath, totalFrames, audioPath, start
|
|
|
11263
11336
|
|
|
11264
11337
|
// src/services/distributed/renderChunk.ts
|
|
11265
11338
|
import { randomBytes } from "node:crypto";
|
|
11266
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync12, readdirSync as readdirSync9, rmSync as
|
|
11339
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync12, readdirSync as readdirSync9, rmSync as rmSync11, writeFileSync as writeFileSync11 } from "node:fs";
|
|
11267
11340
|
import { cpus } from "node:os";
|
|
11268
11341
|
import { join as join27 } from "node:path";
|
|
11269
11342
|
import {
|
|
@@ -11718,7 +11791,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11718
11791
|
// first frame is an IDR keyframe and concat-copy at assemble time
|
|
11719
11792
|
// round-trips losslessly.
|
|
11720
11793
|
lockGopForChunkConcat: !isPngSequence,
|
|
11721
|
-
gopSize: framesInChunk
|
|
11794
|
+
gopSize: chunkGopFrames(framesInChunk, encoder.keyframeIntervalFrames),
|
|
11795
|
+
maxBitrate: encoder.maxBitrate,
|
|
11796
|
+
bufferSize: encoder.bufferSize
|
|
11722
11797
|
};
|
|
11723
11798
|
const captureStarted = Date.now();
|
|
11724
11799
|
captureMode = await runCaptureWithScreenshotFallback({
|
|
@@ -11790,7 +11865,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11790
11865
|
return observedChunkCaptureMode(capturePerfs);
|
|
11791
11866
|
},
|
|
11792
11867
|
resetForScreenshotRetry: () => {
|
|
11793
|
-
|
|
11868
|
+
rmSync11(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11794
11869
|
mkdirSync14(framesDir, { recursive: true });
|
|
11795
11870
|
capturePerfs.length = 0;
|
|
11796
11871
|
captureAttempts.length = 0;
|
|
@@ -11833,8 +11908,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11833
11908
|
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
11834
11909
|
timeoutMs: cfg.ffmpegProcessTimeout
|
|
11835
11910
|
});
|
|
11836
|
-
|
|
11837
|
-
|
|
11911
|
+
rmSync11(videoOnlyPath, { force: true });
|
|
11912
|
+
rmSync11(audioScratchPath, { force: true });
|
|
11838
11913
|
}
|
|
11839
11914
|
encodeStageMs = Date.now() - encodeStarted;
|
|
11840
11915
|
} finally {
|
|
@@ -11884,8 +11959,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
11884
11959
|
writeFileSync11(perfPath, `${JSON.stringify(perfPayload, null, 2)}
|
|
11885
11960
|
`, "utf-8");
|
|
11886
11961
|
try {
|
|
11887
|
-
|
|
11888
|
-
|
|
11962
|
+
rmSync11(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11963
|
+
rmSync11(videoFramesDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
11889
11964
|
} catch (err) {
|
|
11890
11965
|
log.warn("[renderChunk] failed to remove work dir", {
|
|
11891
11966
|
workDir,
|
|
@@ -11920,7 +11995,7 @@ async function renderChunkV2(planV2Dir, chunkIndex, outputChunkPath, options) {
|
|
|
11920
11995
|
materializePlanV2Target(planV2Dir, { role: "chunk", chunkIndex }, materializedPlanDir);
|
|
11921
11996
|
return await renderChunk(materializedPlanDir, chunkIndex, outputChunkPath, options);
|
|
11922
11997
|
} finally {
|
|
11923
|
-
|
|
11998
|
+
rmSync12(workRoot, { recursive: true, force: true });
|
|
11924
11999
|
}
|
|
11925
12000
|
}
|
|
11926
12001
|
async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
@@ -11940,7 +12015,7 @@ async function assembleV2(planV2Dir, chunkPaths, outputPath, options) {
|
|
|
11940
12015
|
options
|
|
11941
12016
|
);
|
|
11942
12017
|
} finally {
|
|
11943
|
-
|
|
12018
|
+
rmSync12(workRoot, { recursive: true, force: true });
|
|
11944
12019
|
}
|
|
11945
12020
|
}
|
|
11946
12021
|
|
|
@@ -12033,6 +12108,20 @@ function validateDistributedRenderConfig(config) {
|
|
|
12033
12108
|
`must look like "10M" or "5000k"; got ${JSON.stringify(config.bitrate)}`
|
|
12034
12109
|
);
|
|
12035
12110
|
}
|
|
12111
|
+
try {
|
|
12112
|
+
validateDeliveryEncoderConfig({
|
|
12113
|
+
format: config.format,
|
|
12114
|
+
fps: { num: config.fps, den: 1 },
|
|
12115
|
+
keyframeIntervalSeconds: config.keyframeIntervalSeconds,
|
|
12116
|
+
maxBitrate: config.maxBitrate,
|
|
12117
|
+
bufferSize: config.bufferSize
|
|
12118
|
+
});
|
|
12119
|
+
} catch (error) {
|
|
12120
|
+
throw new InvalidConfigError(
|
|
12121
|
+
"config.maxBitrate",
|
|
12122
|
+
error instanceof Error ? error.message : String(error)
|
|
12123
|
+
);
|
|
12124
|
+
}
|
|
12036
12125
|
if (config.chunkSize !== void 0) {
|
|
12037
12126
|
if (!Number.isInteger(config.chunkSize) || config.chunkSize < 1) {
|
|
12038
12127
|
throw new InvalidConfigError(
|