@hyperframes/producer 0.8.176 → 0.8.178
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.d.ts +3 -3
- package/dist/distributed.js +219 -68
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +57 -55
- package/dist/index.js +181 -53
- package/dist/public-server.js +29 -13
- package/dist/services/distributed/chunkFrames.d.ts +4 -0
- package/dist/services/distributed/plan.d.ts +8 -4
- package/dist/services/distributed/planV2.d.ts +15 -2
- package/dist/services/distributed/planV2Publisher.d.ts +1 -0
- package/dist/services/distributed/projectHash.d.ts +13 -8
- package/dist/services/htmlCompiler.d.ts +2 -0
- package/dist/services/render/stages/audioStage.d.ts +1 -0
- package/dist/services/render/stages/compileStage.d.ts +2 -0
- package/dist/services/render/stages/extractVideosStage.d.ts +1 -0
- package/dist/services/render/stages/freezePlan.d.ts +8 -0
- package/package.json +7 -7
- package/dist/services/audioExtractor.d.ts +0 -38
package/dist/index.js
CHANGED
|
@@ -1387,9 +1387,9 @@ import {
|
|
|
1387
1387
|
} from "node:fs";
|
|
1388
1388
|
import { hostname, uptime } from "node:os";
|
|
1389
1389
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1390
|
+
import { RENDER_TEMP_DIR } from "@hyperframes/core/project-folders";
|
|
1390
1391
|
var OWNER_FILE = "owner.json";
|
|
1391
1392
|
var TRANSACTION_BACKUP = "backup";
|
|
1392
|
-
var RENDER_TEMP_DIR = /^(work-[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}|hf-render|\..+\.hf-transaction)-[A-Za-z0-9]{6}$/;
|
|
1393
1393
|
var RENDER_JOB_DIR = /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/;
|
|
1394
1394
|
function pidScope() {
|
|
1395
1395
|
const attempt = (read) => {
|
|
@@ -6067,7 +6067,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
6067
6067
|
}
|
|
6068
6068
|
return false;
|
|
6069
6069
|
}
|
|
6070
|
-
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log) {
|
|
6070
|
+
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
6071
6071
|
let filePath = src;
|
|
6072
6072
|
if (isHttpUrl(src)) {
|
|
6073
6073
|
if (!existsSync10(downloadDir)) mkdirSync6(downloadDir, { recursive: true });
|
|
@@ -6081,8 +6081,9 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6081
6081
|
} else {
|
|
6082
6082
|
filePath = decodedUrlPath(src);
|
|
6083
6083
|
if (!filePath.startsWith("/")) filePath = join13(baseDir, filePath);
|
|
6084
|
+
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
6084
6085
|
}
|
|
6085
|
-
if (!existsSync10(filePath)) {
|
|
6086
|
+
if (!isHttpUrl(filePath) && !existsSync10(filePath)) {
|
|
6086
6087
|
return { duration: null, resolvedPath: filePath };
|
|
6087
6088
|
}
|
|
6088
6089
|
const withSrcContext = (error) => {
|
|
@@ -6145,7 +6146,7 @@ function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
|
6145
6146
|
}
|
|
6146
6147
|
return changed ? document2.toString() : html;
|
|
6147
6148
|
}
|
|
6148
|
-
async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
6149
|
+
async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocalPath) {
|
|
6149
6150
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
|
|
6150
6151
|
const mediaUnresolved = unresolved.filter(
|
|
6151
6152
|
(el) => (el.tagName === "video" || el.tagName === "audio") && el.src
|
|
@@ -6161,7 +6162,8 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
6161
6162
|
downloadDir,
|
|
6162
6163
|
el.tagName,
|
|
6163
6164
|
el.id,
|
|
6164
|
-
log
|
|
6165
|
+
log,
|
|
6166
|
+
urlByAbsentLocalPath
|
|
6165
6167
|
).then(({ duration }) => ({ id: el.id, duration }))
|
|
6166
6168
|
)
|
|
6167
6169
|
);
|
|
@@ -6203,7 +6205,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
6203
6205
|
compiledHtml = compiledHtml.replace(/(<audio\b[^>]*)\s+crossorigin(?:=["'][^"']*["'])?/gi, "$1");
|
|
6204
6206
|
return { html: compiledHtml, unresolvedCompositions };
|
|
6205
6207
|
}
|
|
6206
|
-
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set()) {
|
|
6208
|
+
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
6207
6209
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
6208
6210
|
const { document: document2 } = parseHTML4(html);
|
|
6209
6211
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
@@ -6229,13 +6231,16 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6229
6231
|
const { html: compiledSub } = await compileHtmlFile(
|
|
6230
6232
|
item.rawSubHtml,
|
|
6231
6233
|
dirname6(item.filePath),
|
|
6232
|
-
downloadDir
|
|
6234
|
+
downloadDir,
|
|
6235
|
+
void 0,
|
|
6236
|
+
urlByAbsentLocalPath
|
|
6233
6237
|
);
|
|
6234
6238
|
const nested = await parseSubCompositions(
|
|
6235
6239
|
compiledSub,
|
|
6236
6240
|
projectDir,
|
|
6237
6241
|
downloadDir,
|
|
6238
|
-
item.nestedVisited
|
|
6242
|
+
item.nestedVisited,
|
|
6243
|
+
urlByAbsentLocalPath
|
|
6239
6244
|
);
|
|
6240
6245
|
return {
|
|
6241
6246
|
srcPath: item.srcPath,
|
|
@@ -6993,9 +6998,16 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
6993
6998
|
rawHtml,
|
|
6994
6999
|
projectDir,
|
|
6995
7000
|
downloadDir,
|
|
6996
|
-
options.log
|
|
7001
|
+
options.log,
|
|
7002
|
+
options.urlByAbsentLocalPath
|
|
7003
|
+
);
|
|
7004
|
+
const { subCompositions } = await parseSubCompositions(
|
|
7005
|
+
compiledHtml,
|
|
7006
|
+
projectDir,
|
|
7007
|
+
downloadDir,
|
|
7008
|
+
void 0,
|
|
7009
|
+
options.urlByAbsentLocalPath
|
|
6997
7010
|
);
|
|
6998
|
-
const { subCompositions } = await parseSubCompositions(compiledHtml, projectDir, downloadDir);
|
|
6999
7011
|
const fullHtml = ensureFullDocument(compiledHtml);
|
|
7000
7012
|
const inlinedHtml = inlineSubCompositions(
|
|
7001
7013
|
fullHtml,
|
|
@@ -7491,6 +7503,7 @@ async function runCompileStage(input) {
|
|
|
7491
7503
|
allowSystemFontCapture,
|
|
7492
7504
|
abortSignal,
|
|
7493
7505
|
variables: input.variables,
|
|
7506
|
+
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7494
7507
|
animatedGifCacheDir: cfg.extractCacheDir ? join14(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7495
7508
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7496
7509
|
});
|
|
@@ -8489,7 +8502,8 @@ async function runExtractVideosStage(input) {
|
|
|
8489
8502
|
abortSignal,
|
|
8490
8503
|
assertNotAborted,
|
|
8491
8504
|
materializeSymlinks,
|
|
8492
|
-
deferRangeExtraction
|
|
8505
|
+
deferRangeExtraction,
|
|
8506
|
+
urlByAbsentLocalPath
|
|
8493
8507
|
} = input;
|
|
8494
8508
|
const stage2Start = Date.now();
|
|
8495
8509
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
@@ -8583,7 +8597,8 @@ async function runExtractVideosStage(input) {
|
|
|
8583
8597
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
8584
8598
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
8585
8599
|
deferRangeExtraction,
|
|
8586
|
-
contentKeyedDirs: [join16(compiledDir, REMOTE_MEDIA_SUBDIR)]
|
|
8600
|
+
contentKeyedDirs: [join16(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
8601
|
+
urlByAbsentLocalPath
|
|
8587
8602
|
},
|
|
8588
8603
|
abortSignal,
|
|
8589
8604
|
cfg,
|
|
@@ -8666,6 +8681,7 @@ async function runAudioStage(input) {
|
|
|
8666
8681
|
audioGain,
|
|
8667
8682
|
audios,
|
|
8668
8683
|
masterFxChain,
|
|
8684
|
+
urlByAbsentLocalPath,
|
|
8669
8685
|
abortSignal,
|
|
8670
8686
|
assertNotAborted,
|
|
8671
8687
|
log
|
|
@@ -8685,7 +8701,7 @@ async function runAudioStage(input) {
|
|
|
8685
8701
|
audioOutputPath,
|
|
8686
8702
|
duration,
|
|
8687
8703
|
abortSignal,
|
|
8688
|
-
{ ffmpegProcessTimeout, audioGain, masterFxChain },
|
|
8704
|
+
{ ffmpegProcessTimeout, audioGain, masterFxChain, urlByAbsentLocalPath },
|
|
8689
8705
|
compiledDir
|
|
8690
8706
|
);
|
|
8691
8707
|
} catch (err) {
|
|
@@ -13990,6 +14006,8 @@ function buildChunkCaptureOptions(input) {
|
|
|
13990
14006
|
// The controller's snapshot, so the first capture sees the in-process renderer's `__hfVariables`.
|
|
13991
14007
|
variables: encoder.variables,
|
|
13992
14008
|
subTimelineWaitMemo: {},
|
|
14009
|
+
skipReadinessVideoIds: input.skipReadinessVideoIds,
|
|
14010
|
+
videoMetadataHints: input.videoMetadataHints,
|
|
13993
14011
|
...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
|
|
13994
14012
|
// A fixed warmup count keeps `beginFrameTimeTicks` host-independent.
|
|
13995
14013
|
lockWarmupTicks: true,
|
|
@@ -13998,6 +14016,19 @@ function buildChunkCaptureOptions(input) {
|
|
|
13998
14016
|
}
|
|
13999
14017
|
async function extractDeferredVideoFramesForChunk(input) {
|
|
14000
14018
|
const deferred = input.planVideos.extracted.filter((video) => video.deferredRange);
|
|
14019
|
+
const remoteSources = input.remoteSources ?? {};
|
|
14020
|
+
for (const path of Object.keys(remoteSources)) {
|
|
14021
|
+
if (!deferred.some((video) => video.deferredRange.sourcePath === path)) {
|
|
14022
|
+
throw new RenderChunkValidationError(
|
|
14023
|
+
MISSING_PLAN_ARTIFACT,
|
|
14024
|
+
`[renderChunk] ${path} is read by URL but no deferred video uses it`
|
|
14025
|
+
);
|
|
14026
|
+
}
|
|
14027
|
+
defaultLogger.info("[renderChunk] reading video by URL", {
|
|
14028
|
+
path,
|
|
14029
|
+
url: remoteSources[path].split("?")[0]
|
|
14030
|
+
});
|
|
14031
|
+
}
|
|
14001
14032
|
if (deferred.length === 0) return [];
|
|
14002
14033
|
const table = createFrameLookupTable2(
|
|
14003
14034
|
input.planVideos.videos,
|
|
@@ -14025,8 +14056,9 @@ async function extractDeferredVideoFramesForChunk(input) {
|
|
|
14025
14056
|
return { ...video, outputDir, framePaths: /* @__PURE__ */ new Map(), ownedByLookup: false };
|
|
14026
14057
|
}
|
|
14027
14058
|
const range = video.deferredRange;
|
|
14028
|
-
const
|
|
14029
|
-
|
|
14059
|
+
const remoteUrl = remoteSources[range.sourcePath];
|
|
14060
|
+
const sourcePath = remoteUrl ?? join28(input.planDir, ...range.sourcePath.split("/"));
|
|
14061
|
+
if (remoteUrl === void 0 && !existsSync18(sourcePath)) {
|
|
14030
14062
|
throw new RenderChunkValidationError(
|
|
14031
14063
|
MISSING_PLAN_ARTIFACT,
|
|
14032
14064
|
`[renderChunk] planDir is missing the source for video ${JSON.stringify(video.videoId)}: ${range.sourcePath}`
|
|
@@ -14366,6 +14398,16 @@ function collectPlanAssetShas(planDir, digests) {
|
|
|
14366
14398
|
}
|
|
14367
14399
|
return { compositionHtml, assets };
|
|
14368
14400
|
}
|
|
14401
|
+
var PLAN_REMOTE_FILES_RELATIVE_PATH = "meta/remote-files.json";
|
|
14402
|
+
function writePlanRemoteFiles(planDir, files) {
|
|
14403
|
+
if (files.length === 0) return;
|
|
14404
|
+
const sorted = [...files].sort((a, b) => a.path < b.path ? -1 : 1);
|
|
14405
|
+
writeFileSync6(join30(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH), JSON.stringify(sorted), "utf-8");
|
|
14406
|
+
}
|
|
14407
|
+
function readPlanRemoteFiles(planDir) {
|
|
14408
|
+
const path = join30(planDir, PLAN_REMOTE_FILES_RELATIVE_PATH);
|
|
14409
|
+
return existsSync19(path) ? JSON.parse(readFileSync10(path, "utf-8")) : [];
|
|
14410
|
+
}
|
|
14369
14411
|
function recomputePlanHashFromPlanDir(planDir, digests) {
|
|
14370
14412
|
const planJsonPath = join30(planDir, "plan.json");
|
|
14371
14413
|
const encoderJsonPath = join30(planDir, "meta", "encoder.json");
|
|
@@ -14947,7 +14989,7 @@ function createVideoSourceShipper(input) {
|
|
|
14947
14989
|
let result = null;
|
|
14948
14990
|
for (const root of stagedRel === null ? [input.workCompiledDir, input.projectDir] : []) {
|
|
14949
14991
|
const rel = pathInside(root, srcPath);
|
|
14950
|
-
if (rel !== null && existsSync20(join32(input.planDir, "compiled", rel))) {
|
|
14992
|
+
if (rel !== null && (existsSync20(join32(input.planDir, "compiled", rel)) || input.isRemote(srcPath))) {
|
|
14951
14993
|
result = ["compiled", ...rel.split(sep2)].join("/");
|
|
14952
14994
|
break;
|
|
14953
14995
|
}
|
|
@@ -15025,8 +15067,17 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
15025
15067
|
logPlanStage(log, "copy", stageStarted);
|
|
15026
15068
|
const finalCompiledDir = join32(planDir, "compiled");
|
|
15027
15069
|
const needsAlpha = outputNeedsAlpha(config.format);
|
|
15070
|
+
const remoteFiles = Object.entries(options.remoteFiles ?? {});
|
|
15071
|
+
const remoteLocalPath = (rel) => join32(projectDir, ...rel.split("/"));
|
|
15072
|
+
const urlByAbsentLocalPath = new Map(
|
|
15073
|
+
remoteFiles.map(([rel, file]) => [remoteLocalPath(rel), file.url])
|
|
15074
|
+
);
|
|
15075
|
+
const localPathByUrl = new Map(
|
|
15076
|
+
remoteFiles.map(([rel, file]) => [file.url, remoteLocalPath(rel)])
|
|
15077
|
+
);
|
|
15028
15078
|
stageStarted = Date.now();
|
|
15029
15079
|
const compileResult = await runCompileStage({
|
|
15080
|
+
urlByAbsentLocalPath,
|
|
15030
15081
|
projectDir,
|
|
15031
15082
|
workDir,
|
|
15032
15083
|
htmlPath,
|
|
@@ -15114,7 +15165,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
15114
15165
|
abortSignal,
|
|
15115
15166
|
assertNotAborted,
|
|
15116
15167
|
materializeSymlinks: true,
|
|
15117
|
-
deferRangeExtraction: options.deferVideoExtraction
|
|
15168
|
+
deferRangeExtraction: options.deferVideoExtraction,
|
|
15169
|
+
urlByAbsentLocalPath
|
|
15118
15170
|
});
|
|
15119
15171
|
logPlanStage(log, "extract", stageStarted);
|
|
15120
15172
|
if (extractResult.failureToEnforce) throw extractResult.failureToEnforce;
|
|
@@ -15137,7 +15189,8 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
15137
15189
|
planDir,
|
|
15138
15190
|
projectDir,
|
|
15139
15191
|
workCompiledDir: compiledDir,
|
|
15140
|
-
workVideoFramesDir: stagedVideoFrames
|
|
15192
|
+
workVideoFramesDir: stagedVideoFrames,
|
|
15193
|
+
isRemote: (srcPath) => urlByAbsentLocalPath.has(srcPath)
|
|
15141
15194
|
});
|
|
15142
15195
|
const sourceRoots = [
|
|
15143
15196
|
[stagedVideoFrames, "video-frames"],
|
|
@@ -15147,17 +15200,28 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
15147
15200
|
const planVideosJson = buildPlanVideosJson({
|
|
15148
15201
|
videos: composition.videos,
|
|
15149
15202
|
compositionEnd: job.duration ?? Number.NaN,
|
|
15150
|
-
extracted: (extractResult.extractionResult?.extracted ?? []).map((ext) =>
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15203
|
+
extracted: (extractResult.extractionResult?.extracted ?? []).map((ext) => {
|
|
15204
|
+
const sourcePath = localPathByUrl.get(ext.srcPath) ?? ext.srcPath;
|
|
15205
|
+
return {
|
|
15206
|
+
videoId: ext.videoId,
|
|
15207
|
+
srcPath: stableSourcePath(sourceRoots, sourcePath),
|
|
15208
|
+
framePattern: ext.framePattern,
|
|
15209
|
+
fps: ext.fps,
|
|
15210
|
+
totalFrames: ext.totalFrames,
|
|
15211
|
+
metadata: ext.metadata,
|
|
15212
|
+
...ext.deferredRange ? { deferredRange: { ...ext.deferredRange, sourcePath: shipVideoSource(sourcePath) } } : {}
|
|
15213
|
+
};
|
|
15214
|
+
})
|
|
15159
15215
|
});
|
|
15160
15216
|
mkdirSync11(join32(planDir, "meta"), { recursive: true });
|
|
15217
|
+
writePlanRemoteFiles(
|
|
15218
|
+
planDir,
|
|
15219
|
+
remoteFiles.map(([rel, { sha256, sizeBytes }]) => ({
|
|
15220
|
+
path: `compiled/${rel}`,
|
|
15221
|
+
sha256,
|
|
15222
|
+
sizeBytes
|
|
15223
|
+
}))
|
|
15224
|
+
);
|
|
15161
15225
|
writeFileSync7(
|
|
15162
15226
|
join32(planDir, PLAN_VIDEOS_META_RELATIVE_PATH),
|
|
15163
15227
|
JSON.stringify(planVideosJson, null, 2),
|
|
@@ -15172,6 +15236,7 @@ async function buildLocalExecutionPlan(projectDir, config, executionPlanDir, opt
|
|
|
15172
15236
|
projectDir,
|
|
15173
15237
|
workDir,
|
|
15174
15238
|
compiledDir: finalCompiledDir,
|
|
15239
|
+
urlByAbsentLocalPath,
|
|
15175
15240
|
duration: audioSeconds,
|
|
15176
15241
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout,
|
|
15177
15242
|
audioGain: cfg.audioGain,
|
|
@@ -16966,17 +17031,20 @@ function artifactTargets(path, videoDependencies, chunksCarryAudio2) {
|
|
|
16966
17031
|
if (path === "plan.json" || path === "meta/chunks.json" || path === "meta/encoder.json") {
|
|
16967
17032
|
return { chunks: "all", assembler: true };
|
|
16968
17033
|
}
|
|
16969
|
-
if (path
|
|
16970
|
-
return { chunks: "all", assembler: false };
|
|
16971
|
-
}
|
|
16972
|
-
if (path.startsWith("video-frames/") || path.startsWith("video-sources/")) {
|
|
17034
|
+
if (path.startsWith("video-frames/") || path.startsWith("video-sources/") || path.startsWith("compiled/") && videoDependencies?.has(path)) {
|
|
16973
17035
|
return {
|
|
16974
17036
|
chunks: videoDependencies === null ? "all" : videoDependencies.get(path) ?? [],
|
|
16975
17037
|
assembler: false
|
|
16976
17038
|
};
|
|
16977
17039
|
}
|
|
17040
|
+
if (path === "meta/composition.json" || path === "meta/videos.json" || path.startsWith("compiled/")) {
|
|
17041
|
+
return { chunks: "all", assembler: false };
|
|
17042
|
+
}
|
|
16978
17043
|
return { chunks: "all", assembler: true };
|
|
16979
17044
|
}
|
|
17045
|
+
function isPlanV2DeferredSourceArtifact(artifact) {
|
|
17046
|
+
return artifact.path.startsWith("compiled/") && artifact.chunks !== "all";
|
|
17047
|
+
}
|
|
16980
17048
|
function listVideoFramePaths(executionPlanDir, videos) {
|
|
16981
17049
|
return videos.extracted.map((video) => {
|
|
16982
17050
|
const outputDir = resolveExtractedVideoOutputDir(executionPlanDir, video.videoId);
|
|
@@ -17139,14 +17207,17 @@ function publishBlobFor(absolutePath, digests) {
|
|
|
17139
17207
|
};
|
|
17140
17208
|
}
|
|
17141
17209
|
async function putBlobs(publisher, blobs) {
|
|
17210
|
+
await putTasks(blobs.map((blob) => () => publisher.putBlob(blob)));
|
|
17211
|
+
}
|
|
17212
|
+
async function putTasks(tasks) {
|
|
17142
17213
|
const uploadSlots = 32;
|
|
17143
17214
|
let next = 0;
|
|
17144
17215
|
let failure;
|
|
17145
17216
|
const upload = async () => {
|
|
17146
|
-
while (!failure && next <
|
|
17147
|
-
const
|
|
17217
|
+
while (!failure && next < tasks.length) {
|
|
17218
|
+
const task = tasks[next++];
|
|
17148
17219
|
try {
|
|
17149
|
-
await
|
|
17220
|
+
await task();
|
|
17150
17221
|
} catch (reason) {
|
|
17151
17222
|
failure ??= { reason };
|
|
17152
17223
|
}
|
|
@@ -17201,6 +17272,15 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
17201
17272
|
...targets
|
|
17202
17273
|
});
|
|
17203
17274
|
}
|
|
17275
|
+
const storedBlobs = /* @__PURE__ */ new Map();
|
|
17276
|
+
for (const remote of readPlanRemoteFiles(executionPlanDir)) {
|
|
17277
|
+
const { sha256, sizeBytes } = remote;
|
|
17278
|
+
if (!blobs.has(sha256)) storedBlobs.set(sha256, { sha256, sizeBytes });
|
|
17279
|
+
artifacts.push({
|
|
17280
|
+
...remote,
|
|
17281
|
+
...artifactTargets(remote.path, videoDependencyPlan.dependencies, chunksCarryAudio2)
|
|
17282
|
+
});
|
|
17283
|
+
}
|
|
17204
17284
|
const base = {
|
|
17205
17285
|
protocol: PLAN_PROTOCOL_V2,
|
|
17206
17286
|
// Retain the established manifest key byte-for-byte. It now acts as the
|
|
@@ -17222,7 +17302,8 @@ function buildPlanV2Publication(executionPlanDir, digests) {
|
|
|
17222
17302
|
...base,
|
|
17223
17303
|
planHash: computeManifestHash(base)
|
|
17224
17304
|
},
|
|
17225
|
-
blobs: [...blobs.values()]
|
|
17305
|
+
blobs: [...blobs.values()],
|
|
17306
|
+
storedBlobs: [...storedBlobs.values()]
|
|
17226
17307
|
};
|
|
17227
17308
|
}
|
|
17228
17309
|
function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
@@ -17230,6 +17311,9 @@ function createPlanV2FromExecutionPlan(executionPlanDir, planV2Dir) {
|
|
|
17230
17311
|
throw new PlanV2IntegrityError(`output directory already exists: ${planV2Dir}`);
|
|
17231
17312
|
}
|
|
17232
17313
|
const publication = buildPlanV2Publication(executionPlanDir);
|
|
17314
|
+
if (publication.storedBlobs.length > 0) {
|
|
17315
|
+
throw new PlanV2IntegrityError("a local plan v2 directory cannot reference remote files");
|
|
17316
|
+
}
|
|
17233
17317
|
mkdirSync14(dirname15(planV2Dir), { recursive: true });
|
|
17234
17318
|
const tempDir = mkdtempSync6(join38(dirname15(planV2Dir), ".plan-v2-build-"));
|
|
17235
17319
|
try {
|
|
@@ -17259,8 +17343,14 @@ async function publishExecutionPlan(executionPlanDir, publisher, { digests, log
|
|
|
17259
17343
|
let stageStarted = Date.now();
|
|
17260
17344
|
const publication = buildPlanV2Publication(executionPlanDir, digests);
|
|
17261
17345
|
logPlanStage(log, "publication", stageStarted);
|
|
17346
|
+
if (publication.storedBlobs.length > 0 && !publisher.referenceStoredBlob) {
|
|
17347
|
+
throw new PlanV2IntegrityError("this publisher cannot reference remote files");
|
|
17348
|
+
}
|
|
17262
17349
|
stageStarted = Date.now();
|
|
17263
|
-
await
|
|
17350
|
+
await putTasks([
|
|
17351
|
+
...publication.blobs.map((blob) => () => publisher.putBlob(blob)),
|
|
17352
|
+
...publication.storedBlobs.map((blob) => () => publisher.referenceStoredBlob(blob))
|
|
17353
|
+
]);
|
|
17264
17354
|
logPlanStage(log, "upload", stageStarted);
|
|
17265
17355
|
stageStarted = Date.now();
|
|
17266
17356
|
await publisher.commitManifest(canonicalJsonStringify(publication.manifest));
|
|
@@ -17284,6 +17374,7 @@ async function planV2WithPublisher(projectDir, config, publisher, options = {})
|
|
|
17284
17374
|
await buildLocalExecutionPlan(projectDir, config, stagingRoot, {
|
|
17285
17375
|
executionPlanSizeLimitBytes: Number.MAX_SAFE_INTEGER,
|
|
17286
17376
|
deferVideoExtraction: true,
|
|
17377
|
+
remoteFiles: options.remoteFiles,
|
|
17287
17378
|
digests,
|
|
17288
17379
|
// artifactTargets ships every compiled/ file to all chunks, so each is in the manifest.
|
|
17289
17380
|
whileAudioMixes: () => putBlobs(
|
|
@@ -17486,14 +17577,16 @@ function verifyBlob(planV2Dir, artifact) {
|
|
|
17486
17577
|
}
|
|
17487
17578
|
return sourcePath;
|
|
17488
17579
|
}
|
|
17489
|
-
function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
17580
|
+
function materializePlanV2Target(planV2Dir, target, destinationDir, options = {}) {
|
|
17490
17581
|
if (existsSync25(destinationDir)) {
|
|
17491
17582
|
throw new PlanV2IntegrityError(`materialization destination already exists: ${destinationDir}`);
|
|
17492
17583
|
}
|
|
17493
17584
|
const manifest = readPlanV2Manifest(planV2Dir);
|
|
17494
17585
|
const artifacts = listPlanV2ArtifactsForTarget(manifest, target);
|
|
17586
|
+
const remoteSources = options.remoteSources ?? {};
|
|
17587
|
+
assertRemoteSources(remoteSources, artifacts);
|
|
17495
17588
|
const audioArtifact = artifacts.find((artifact) => isPlanAudioArtifactPath(artifact.path));
|
|
17496
|
-
const verified = artifacts.map((artifact) => ({
|
|
17589
|
+
const verified = artifacts.filter((artifact) => !Object.hasOwn(remoteSources, artifact.path)).map((artifact) => ({
|
|
17497
17590
|
artifact,
|
|
17498
17591
|
sourcePath: verifyBlob(planV2Dir, artifact)
|
|
17499
17592
|
}));
|
|
@@ -17508,7 +17601,11 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
17508
17601
|
if (target.role === "chunk") materializeExtractedVideoDirectories(tempDir);
|
|
17509
17602
|
writeFileSync10(
|
|
17510
17603
|
join38(tempDir, PLAN_V2_MATERIALIZATION_MARKER),
|
|
17511
|
-
canonicalJsonStringify({
|
|
17604
|
+
canonicalJsonStringify({
|
|
17605
|
+
manifest,
|
|
17606
|
+
target,
|
|
17607
|
+
...Object.keys(remoteSources).length > 0 ? { remoteSources } : {}
|
|
17608
|
+
}),
|
|
17512
17609
|
"utf-8"
|
|
17513
17610
|
);
|
|
17514
17611
|
renameSync7(tempDir, destinationDir);
|
|
@@ -17529,7 +17626,22 @@ function materializePlanV2Target(planV2Dir, target, destinationDir) {
|
|
|
17529
17626
|
audioPath: target.role === "assembler" ? audioArtifact ? join38(destinationDir, audioArtifact.path) : null : null
|
|
17530
17627
|
};
|
|
17531
17628
|
}
|
|
17629
|
+
function assertRemoteSources(remoteSources, artifacts) {
|
|
17630
|
+
if (!isRecord3(remoteSources)) throw new PlanV2IntegrityError("remoteSources must be an object");
|
|
17631
|
+
for (const [path, url] of Object.entries(remoteSources)) {
|
|
17632
|
+
const artifact = artifacts.find((candidate) => candidate.path === path);
|
|
17633
|
+
if (!artifact || !isPlanV2DeferredSourceArtifact(artifact)) {
|
|
17634
|
+
throw new PlanV2IntegrityError(`remote source is not a selected deferred video: ${path}`);
|
|
17635
|
+
}
|
|
17636
|
+
if (typeof url !== "string" || !/^https?:\/\//.test(url)) {
|
|
17637
|
+
throw new PlanV2IntegrityError(`remote source for ${path} must be an http(s) URL`);
|
|
17638
|
+
}
|
|
17639
|
+
}
|
|
17640
|
+
}
|
|
17532
17641
|
function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
17642
|
+
return readValidatedPlanV2Materialization(planDir, expectedTarget)?.manifest ?? null;
|
|
17643
|
+
}
|
|
17644
|
+
function readValidatedPlanV2Materialization(planDir, expectedTarget) {
|
|
17533
17645
|
const markerPath = join38(planDir, PLAN_V2_MATERIALIZATION_MARKER);
|
|
17534
17646
|
if (!existsSync25(markerPath)) return null;
|
|
17535
17647
|
const marker = readJsonFile(markerPath, "materialization marker");
|
|
@@ -17544,7 +17656,10 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
17544
17656
|
}
|
|
17545
17657
|
const manifest = parsePlanV2Manifest(marker.manifest);
|
|
17546
17658
|
const required = listPlanV2ArtifactsForTarget(manifest, expectedTarget);
|
|
17659
|
+
const remoteSources = marker.remoteSources ?? {};
|
|
17660
|
+
assertRemoteSources(remoteSources, required);
|
|
17547
17661
|
for (const artifact of required) {
|
|
17662
|
+
if (Object.hasOwn(remoteSources, artifact.path)) continue;
|
|
17548
17663
|
const path = join38(planDir, ...artifact.path.split("/"));
|
|
17549
17664
|
if (!existsSync25(path) || statSync10(path).size !== artifact.sizeBytes) {
|
|
17550
17665
|
throw new PlanV2IntegrityError(
|
|
@@ -17555,7 +17670,7 @@ function validatePlanV2MaterializedTarget(planDir, expectedTarget) {
|
|
|
17555
17670
|
throw new PlanV2IntegrityError(`materialized artifact hash mismatch: ${artifact.path}`);
|
|
17556
17671
|
}
|
|
17557
17672
|
}
|
|
17558
|
-
return manifest;
|
|
17673
|
+
return { manifest, remoteSources };
|
|
17559
17674
|
}
|
|
17560
17675
|
|
|
17561
17676
|
// src/services/distributed/planV2Execution.ts
|
|
@@ -18430,10 +18545,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18430
18545
|
const plan2 = JSON.parse(readFileSync14(planJsonPath, "utf-8"));
|
|
18431
18546
|
readPlanProtocolV1(plan2, DISTRIBUTED_RENDER_CAPABILITIES.roles.chunk);
|
|
18432
18547
|
const planHashStarted = Date.now();
|
|
18433
|
-
const
|
|
18548
|
+
const v2Materialization = readValidatedPlanV2Materialization(planDir, {
|
|
18434
18549
|
role: "chunk",
|
|
18435
18550
|
chunkIndex
|
|
18436
18551
|
});
|
|
18552
|
+
const v2Manifest = v2Materialization?.manifest ?? null;
|
|
18553
|
+
const remoteSources = v2Materialization?.remoteSources ?? {};
|
|
18437
18554
|
for (const required of [encoderJsonPath, chunksJsonPath]) {
|
|
18438
18555
|
if (!existsSync27(required)) {
|
|
18439
18556
|
throw new RenderChunkValidationError(
|
|
@@ -18535,6 +18652,24 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18535
18652
|
const framesDir = join41(workDir, "captured-frames");
|
|
18536
18653
|
mkdirSync17(framesDir, { recursive: true });
|
|
18537
18654
|
const videoFramesDir = `${workDir}.video-frames`;
|
|
18655
|
+
const deferredVideoFrames = planVideos ? await extractDeferredVideoFramesForChunk({
|
|
18656
|
+
planDir,
|
|
18657
|
+
planVideos,
|
|
18658
|
+
frames: Array.from(
|
|
18659
|
+
{ length: slice.endFrame - slice.startFrame },
|
|
18660
|
+
(_, i) => slice.startFrame + i
|
|
18661
|
+
),
|
|
18662
|
+
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
18663
|
+
outputRoot: videoFramesDir,
|
|
18664
|
+
cfg,
|
|
18665
|
+
remoteSources
|
|
18666
|
+
}) : [];
|
|
18667
|
+
const remoteVideoIds = new Set(
|
|
18668
|
+
planVideos?.extracted.filter(
|
|
18669
|
+
(video) => video.deferredRange && Object.hasOwn(remoteSources, video.deferredRange.sourcePath)
|
|
18670
|
+
).map((video) => video.videoId)
|
|
18671
|
+
);
|
|
18672
|
+
const remoteVideos = deferredVideoFrames.filter((video) => remoteVideoIds.has(video.videoId));
|
|
18538
18673
|
const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? createFrameLookupTable5(
|
|
18539
18674
|
planVideos.videos,
|
|
18540
18675
|
[
|
|
@@ -18543,17 +18678,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18543
18678
|
planVideos.extracted.filter((video) => !video.deferredRange),
|
|
18544
18679
|
v2Manifest === null ? "dense-v1" : "sparse-v2"
|
|
18545
18680
|
),
|
|
18546
|
-
...
|
|
18547
|
-
planDir,
|
|
18548
|
-
planVideos,
|
|
18549
|
-
frames: Array.from(
|
|
18550
|
-
{ length: slice.endFrame - slice.startFrame },
|
|
18551
|
-
(_, i) => slice.startFrame + i
|
|
18552
|
-
),
|
|
18553
|
-
fps: { num: plan2.dimensions.fpsNum, den: plan2.dimensions.fpsDen },
|
|
18554
|
-
outputRoot: videoFramesDir,
|
|
18555
|
-
cfg
|
|
18556
|
-
})
|
|
18681
|
+
...deferredVideoFrames
|
|
18557
18682
|
],
|
|
18558
18683
|
resolveRenderFpsConfig(job.config.fps).value
|
|
18559
18684
|
) : null;
|
|
@@ -18571,7 +18696,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
|
|
|
18571
18696
|
dimensions: plan2.dimensions,
|
|
18572
18697
|
encoder,
|
|
18573
18698
|
videoCount: planVideos?.videos.length ?? 0,
|
|
18574
|
-
html: readFileSync14(join41(compiledDir, "index.html"), "utf-8")
|
|
18699
|
+
html: readFileSync14(join41(compiledDir, "index.html"), "utf-8"),
|
|
18700
|
+
// The plan ships a deferred source only to chunks that show it; don't wait on the rest.
|
|
18701
|
+
skipReadinessVideoIds: deferredVideoFrames.filter((video) => video.framePaths.size === 0 || remoteVideoIds.has(video.videoId)).map((video) => video.videoId),
|
|
18702
|
+
videoMetadataHints: collectVideoMetadataHints(remoteVideos)
|
|
18575
18703
|
});
|
|
18576
18704
|
const chunkWorkerCount = calculateOptimalWorkers2(framesInChunk, void 0, cfg);
|
|
18577
18705
|
let session = null;
|
package/dist/public-server.js
CHANGED
|
@@ -1407,9 +1407,9 @@ import {
|
|
|
1407
1407
|
} from "node:fs";
|
|
1408
1408
|
import { hostname, uptime } from "node:os";
|
|
1409
1409
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
1410
|
+
import { RENDER_TEMP_DIR } from "@hyperframes/core/project-folders";
|
|
1410
1411
|
var OWNER_FILE = "owner.json";
|
|
1411
1412
|
var TRANSACTION_BACKUP = "backup";
|
|
1412
|
-
var RENDER_TEMP_DIR = /^(work-[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}|hf-render|\..+\.hf-transaction)-[A-Za-z0-9]{6}$/;
|
|
1413
1413
|
function pidScope() {
|
|
1414
1414
|
const attempt = (read) => {
|
|
1415
1415
|
try {
|
|
@@ -6086,7 +6086,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
6086
6086
|
}
|
|
6087
6087
|
return false;
|
|
6088
6088
|
}
|
|
6089
|
-
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log) {
|
|
6089
|
+
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
6090
6090
|
let filePath = src;
|
|
6091
6091
|
if (isHttpUrl(src)) {
|
|
6092
6092
|
if (!existsSync10(downloadDir)) mkdirSync6(downloadDir, { recursive: true });
|
|
@@ -6100,8 +6100,9 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6100
6100
|
} else {
|
|
6101
6101
|
filePath = decodedUrlPath(src);
|
|
6102
6102
|
if (!filePath.startsWith("/")) filePath = join13(baseDir, filePath);
|
|
6103
|
+
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
6103
6104
|
}
|
|
6104
|
-
if (!existsSync10(filePath)) {
|
|
6105
|
+
if (!isHttpUrl(filePath) && !existsSync10(filePath)) {
|
|
6105
6106
|
return { duration: null, resolvedPath: filePath };
|
|
6106
6107
|
}
|
|
6107
6108
|
const withSrcContext = (error) => {
|
|
@@ -6164,7 +6165,7 @@ function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
|
6164
6165
|
}
|
|
6165
6166
|
return changed ? document2.toString() : html;
|
|
6166
6167
|
}
|
|
6167
|
-
async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
6168
|
+
async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocalPath) {
|
|
6168
6169
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
|
|
6169
6170
|
const mediaUnresolved = unresolved.filter(
|
|
6170
6171
|
(el) => (el.tagName === "video" || el.tagName === "audio") && el.src
|
|
@@ -6180,7 +6181,8 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
6180
6181
|
downloadDir,
|
|
6181
6182
|
el.tagName,
|
|
6182
6183
|
el.id,
|
|
6183
|
-
log
|
|
6184
|
+
log,
|
|
6185
|
+
urlByAbsentLocalPath
|
|
6184
6186
|
).then(({ duration }) => ({ id: el.id, duration }))
|
|
6185
6187
|
)
|
|
6186
6188
|
);
|
|
@@ -6222,7 +6224,7 @@ async function compileHtmlFile(html, baseDir, downloadDir, log) {
|
|
|
6222
6224
|
compiledHtml = compiledHtml.replace(/(<audio\b[^>]*)\s+crossorigin(?:=["'][^"']*["'])?/gi, "$1");
|
|
6223
6225
|
return { html: compiledHtml, unresolvedCompositions };
|
|
6224
6226
|
}
|
|
6225
|
-
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set()) {
|
|
6227
|
+
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
6226
6228
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
6227
6229
|
const { document: document2 } = parseHTML4(html);
|
|
6228
6230
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
@@ -6248,13 +6250,16 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6248
6250
|
const { html: compiledSub } = await compileHtmlFile(
|
|
6249
6251
|
item.rawSubHtml,
|
|
6250
6252
|
dirname6(item.filePath),
|
|
6251
|
-
downloadDir
|
|
6253
|
+
downloadDir,
|
|
6254
|
+
void 0,
|
|
6255
|
+
urlByAbsentLocalPath
|
|
6252
6256
|
);
|
|
6253
6257
|
const nested = await parseSubCompositions(
|
|
6254
6258
|
compiledSub,
|
|
6255
6259
|
projectDir,
|
|
6256
6260
|
downloadDir,
|
|
6257
|
-
item.nestedVisited
|
|
6261
|
+
item.nestedVisited,
|
|
6262
|
+
urlByAbsentLocalPath
|
|
6258
6263
|
);
|
|
6259
6264
|
return {
|
|
6260
6265
|
srcPath: item.srcPath,
|
|
@@ -7012,9 +7017,16 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7012
7017
|
rawHtml,
|
|
7013
7018
|
projectDir,
|
|
7014
7019
|
downloadDir,
|
|
7015
|
-
options.log
|
|
7020
|
+
options.log,
|
|
7021
|
+
options.urlByAbsentLocalPath
|
|
7022
|
+
);
|
|
7023
|
+
const { subCompositions } = await parseSubCompositions(
|
|
7024
|
+
compiledHtml,
|
|
7025
|
+
projectDir,
|
|
7026
|
+
downloadDir,
|
|
7027
|
+
void 0,
|
|
7028
|
+
options.urlByAbsentLocalPath
|
|
7016
7029
|
);
|
|
7017
|
-
const { subCompositions } = await parseSubCompositions(compiledHtml, projectDir, downloadDir);
|
|
7018
7030
|
const fullHtml = ensureFullDocument(compiledHtml);
|
|
7019
7031
|
const inlinedHtml = inlineSubCompositions(
|
|
7020
7032
|
fullHtml,
|
|
@@ -7510,6 +7522,7 @@ async function runCompileStage(input) {
|
|
|
7510
7522
|
allowSystemFontCapture,
|
|
7511
7523
|
abortSignal,
|
|
7512
7524
|
variables: input.variables,
|
|
7525
|
+
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7513
7526
|
animatedGifCacheDir: cfg.extractCacheDir ? join14(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7514
7527
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7515
7528
|
});
|
|
@@ -8472,7 +8485,8 @@ async function runExtractVideosStage(input) {
|
|
|
8472
8485
|
abortSignal,
|
|
8473
8486
|
assertNotAborted,
|
|
8474
8487
|
materializeSymlinks,
|
|
8475
|
-
deferRangeExtraction
|
|
8488
|
+
deferRangeExtraction,
|
|
8489
|
+
urlByAbsentLocalPath
|
|
8476
8490
|
} = input;
|
|
8477
8491
|
const stage2Start = Date.now();
|
|
8478
8492
|
const extractionPolicy = resolveVideoExtractionPolicy();
|
|
@@ -8566,7 +8580,8 @@ async function runExtractVideosStage(input) {
|
|
|
8566
8580
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
8567
8581
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
8568
8582
|
deferRangeExtraction,
|
|
8569
|
-
contentKeyedDirs: [join16(compiledDir, REMOTE_MEDIA_SUBDIR)]
|
|
8583
|
+
contentKeyedDirs: [join16(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
8584
|
+
urlByAbsentLocalPath
|
|
8570
8585
|
},
|
|
8571
8586
|
abortSignal,
|
|
8572
8587
|
cfg,
|
|
@@ -8649,6 +8664,7 @@ async function runAudioStage(input) {
|
|
|
8649
8664
|
audioGain,
|
|
8650
8665
|
audios,
|
|
8651
8666
|
masterFxChain,
|
|
8667
|
+
urlByAbsentLocalPath,
|
|
8652
8668
|
abortSignal,
|
|
8653
8669
|
assertNotAborted,
|
|
8654
8670
|
log
|
|
@@ -8668,7 +8684,7 @@ async function runAudioStage(input) {
|
|
|
8668
8684
|
audioOutputPath,
|
|
8669
8685
|
duration,
|
|
8670
8686
|
abortSignal,
|
|
8671
|
-
{ ffmpegProcessTimeout, audioGain, masterFxChain },
|
|
8687
|
+
{ ffmpegProcessTimeout, audioGain, masterFxChain, urlByAbsentLocalPath },
|
|
8672
8688
|
compiledDir
|
|
8673
8689
|
);
|
|
8674
8690
|
} catch (err) {
|