@hyperframes/producer 0.8.203 → 0.8.205
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 +396 -35
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +17 -17
- package/dist/index.js +196 -76
- package/dist/public-server.js +166 -53
- package/dist/services/render/observability.d.ts +23 -1
- package/dist/services/render/stages/captureStage.d.ts +3 -0
- package/dist/services/renderOrchestrator.d.ts +6 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1238,6 +1238,7 @@ function buildRenderErrorDetails(input) {
|
|
|
1238
1238
|
|
|
1239
1239
|
// src/services/render/renderEventPublisher.ts
|
|
1240
1240
|
import { cloneCaptureWarnings } from "@hyperframes/engine";
|
|
1241
|
+
var FLUSH_TIMEOUT_MS = 1e4;
|
|
1241
1242
|
function snapshotJob(job) {
|
|
1242
1243
|
return {
|
|
1243
1244
|
...job,
|
|
@@ -1267,7 +1268,15 @@ var OrderedRenderEventPublisher = class {
|
|
|
1267
1268
|
});
|
|
1268
1269
|
}
|
|
1269
1270
|
async flush() {
|
|
1270
|
-
|
|
1271
|
+
let timer;
|
|
1272
|
+
const drained = await Promise.race([
|
|
1273
|
+
this.tail.then(() => true),
|
|
1274
|
+
new Promise((resolve12) => {
|
|
1275
|
+
timer = setTimeout(() => resolve12(false), FLUSH_TIMEOUT_MS);
|
|
1276
|
+
})
|
|
1277
|
+
]);
|
|
1278
|
+
clearTimeout(timer);
|
|
1279
|
+
if (!drained) this.log.warn("Render event sink did not drain; continuing without it");
|
|
1271
1280
|
}
|
|
1272
1281
|
};
|
|
1273
1282
|
function publishRenderFailure(job, failure, onProgress) {
|
|
@@ -5100,6 +5109,7 @@ function shouldFallbackToScreenshotAfterCalibrationError(error) {
|
|
|
5100
5109
|
// src/services/render/observability.ts
|
|
5101
5110
|
import { createHash as createHash3 } from "node:crypto";
|
|
5102
5111
|
import { redactTelemetryString } from "@hyperframes/core";
|
|
5112
|
+
import { CaptureFailure as CaptureFailure2 } from "@hyperframes/engine";
|
|
5103
5113
|
var MAX_EVENTS = 160;
|
|
5104
5114
|
var ALLOWED_STRING_DATA_KEYS = /* @__PURE__ */ new Set([
|
|
5105
5115
|
"browserGpuMode",
|
|
@@ -5332,6 +5342,56 @@ function heartbeatTargetMs(index) {
|
|
|
5332
5342
|
const overflow = index - HEARTBEAT_RAMP_MS.length + 1;
|
|
5333
5343
|
return HEARTBEAT_RAMP_END_MS + overflow * HEARTBEAT_REPEAT_MS;
|
|
5334
5344
|
}
|
|
5345
|
+
var RenderStageStalledError = class extends CaptureFailure2 {
|
|
5346
|
+
code = "RENDER_STAGE_STALLED";
|
|
5347
|
+
owner = "system";
|
|
5348
|
+
retryable = false;
|
|
5349
|
+
constructor(phase, silentMs, progress) {
|
|
5350
|
+
super({
|
|
5351
|
+
kind: "protocol_timeout",
|
|
5352
|
+
message: `[Render] ${phase} stalled: no progress for ${silentMs}ms (stuck at ${progress}).`
|
|
5353
|
+
});
|
|
5354
|
+
this.name = "RenderStageStalledError";
|
|
5355
|
+
}
|
|
5356
|
+
};
|
|
5357
|
+
var STALL_POLL_MS = 5e3;
|
|
5358
|
+
function watchStall(phase, rule) {
|
|
5359
|
+
let last = rule.progress();
|
|
5360
|
+
let budgetMs = rule.firstProgressMs;
|
|
5361
|
+
let lastChangeAt = Date.now();
|
|
5362
|
+
let attempt;
|
|
5363
|
+
let timer;
|
|
5364
|
+
const restartWindow = (ms) => {
|
|
5365
|
+
last = rule.progress();
|
|
5366
|
+
budgetMs = ms;
|
|
5367
|
+
lastChangeAt = Date.now();
|
|
5368
|
+
};
|
|
5369
|
+
const tripped = new Promise((_, reject) => {
|
|
5370
|
+
timer = setInterval(
|
|
5371
|
+
() => {
|
|
5372
|
+
const current = rule.progress();
|
|
5373
|
+
if (current !== last) return restartWindow(rule.noProgressMs);
|
|
5374
|
+
if (Date.now() - lastChangeAt < budgetMs) return;
|
|
5375
|
+
const error = new RenderStageStalledError(phase, budgetMs, current);
|
|
5376
|
+
if (attempt && !attempt.signal.aborted) {
|
|
5377
|
+
attempt.abort(error);
|
|
5378
|
+
return restartWindow(rule.noProgressMs);
|
|
5379
|
+
}
|
|
5380
|
+
clearInterval(timer);
|
|
5381
|
+
reject(error);
|
|
5382
|
+
},
|
|
5383
|
+
Math.min(STALL_POLL_MS, rule.noProgressMs, rule.firstProgressMs)
|
|
5384
|
+
);
|
|
5385
|
+
});
|
|
5386
|
+
const openAttempt = (parent) => {
|
|
5387
|
+
attempt = new AbortController();
|
|
5388
|
+
restartWindow(rule.firstProgressMs);
|
|
5389
|
+
return parent ? AbortSignal.any([parent, attempt.signal]) : attempt.signal;
|
|
5390
|
+
};
|
|
5391
|
+
const stalledBy = () => attempt?.signal.aborted ? attempt.signal.reason : void 0;
|
|
5392
|
+
return { tripped, openAttempt, stalledBy, stop: () => clearInterval(timer) };
|
|
5393
|
+
}
|
|
5394
|
+
var openUnwatchedAttempt = (parent) => parent ?? new AbortController().signal;
|
|
5335
5395
|
async function observeRenderStage(recorder, phase, data, fn, options = {}) {
|
|
5336
5396
|
const heartbeatMessage = options.heartbeatMessage ?? "stage still running";
|
|
5337
5397
|
const startedAt = recorder.stageStart(phase, data);
|
|
@@ -5353,15 +5413,19 @@ async function observeRenderStage(recorder, phase, data, fn, options = {}) {
|
|
|
5353
5413
|
heartbeatTimer.unref?.();
|
|
5354
5414
|
};
|
|
5355
5415
|
scheduleNextHeartbeat();
|
|
5416
|
+
const stall = options.stall ? watchStall(phase, options.stall) : void 0;
|
|
5356
5417
|
const clearHeartbeats = () => {
|
|
5357
5418
|
clearTimeout(heartbeatTimer);
|
|
5419
|
+
stall?.stop();
|
|
5358
5420
|
};
|
|
5359
5421
|
try {
|
|
5360
|
-
const
|
|
5422
|
+
const run = fn(stall?.openAttempt ?? openUnwatchedAttempt);
|
|
5423
|
+
const result = await (stall ? Promise.race([run, stall.tripped]) : run);
|
|
5361
5424
|
clearHeartbeats();
|
|
5362
5425
|
recorder.stageEnd(phase, startedAt, data);
|
|
5363
5426
|
return result;
|
|
5364
|
-
} catch (
|
|
5427
|
+
} catch (caught) {
|
|
5428
|
+
const error = stall?.stalledBy() ?? caught;
|
|
5365
5429
|
clearHeartbeats();
|
|
5366
5430
|
recorder.stageError(phase, startedAt, error, data);
|
|
5367
5431
|
throw error;
|
|
@@ -5545,6 +5609,8 @@ import {
|
|
|
5545
5609
|
readDeclaredDefaults,
|
|
5546
5610
|
parseHostVariableValues,
|
|
5547
5611
|
headStyleRuns,
|
|
5612
|
+
isAlwaysAppliedStyle,
|
|
5613
|
+
dedupeFontFaceRules,
|
|
5548
5614
|
inlineScriptRuns,
|
|
5549
5615
|
parsesAsScript,
|
|
5550
5616
|
styleElementsFor,
|
|
@@ -5837,12 +5903,12 @@ function resolveClipWindowSeconds(img, compositionDuration) {
|
|
|
5837
5903
|
}
|
|
5838
5904
|
return null;
|
|
5839
5905
|
}
|
|
5840
|
-
function resolvePreparedPlayback(metadata,
|
|
5906
|
+
function resolvePreparedPlayback(metadata, loopOverride, windowSeconds) {
|
|
5841
5907
|
const gifDuration = metadata.durationSeconds > 0 ? metadata.durationSeconds : null;
|
|
5842
5908
|
let loopIterations = 1;
|
|
5843
|
-
if (
|
|
5909
|
+
if (resolveLoop(metadata, loopOverride)) {
|
|
5844
5910
|
loopIterations = windowSeconds != null && gifDuration != null ? Math.min(MAX_LOOP_ITERATIONS, Math.max(1, Math.ceil(windowSeconds / gifDuration))) : 1;
|
|
5845
|
-
} else if (metadata.loopCount != null && metadata.loopCount > 1) {
|
|
5911
|
+
} else if (loopOverride == null && metadata.loopCount != null && metadata.loopCount > 1) {
|
|
5846
5912
|
loopIterations = Math.min(MAX_LOOP_ITERATIONS, metadata.loopCount);
|
|
5847
5913
|
}
|
|
5848
5914
|
let padSeconds = 0;
|
|
@@ -6009,9 +6075,14 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6009
6075
|
const bytes = readFileSync5(sourcePath);
|
|
6010
6076
|
const metadata = parseAnimatedGifMetadata(bytes);
|
|
6011
6077
|
if (!metadata?.animated) continue;
|
|
6012
|
-
const
|
|
6078
|
+
const loopOverride = readLoopOverride(img);
|
|
6079
|
+
const loop = resolveLoop(metadata, loopOverride);
|
|
6013
6080
|
const windowSeconds = resolveClipWindowSeconds(img, compositionDuration);
|
|
6014
|
-
const { loopIterations, padSeconds } = resolvePreparedPlayback(
|
|
6081
|
+
const { loopIterations, padSeconds } = resolvePreparedPlayback(
|
|
6082
|
+
metadata,
|
|
6083
|
+
loopOverride,
|
|
6084
|
+
windowSeconds
|
|
6085
|
+
);
|
|
6015
6086
|
const hash = computePreparedGifHash(bytes, loopIterations, padSeconds);
|
|
6016
6087
|
const filename = `${CACHE_SCHEMA}-${hash.slice(0, 24)}.webm`;
|
|
6017
6088
|
const cachePath = join13(cacheDir, filename);
|
|
@@ -6677,6 +6748,11 @@ function coalesceHeadStylesAndBodyScripts(html) {
|
|
|
6677
6748
|
const head = document2.querySelector("head");
|
|
6678
6749
|
const body = document2.querySelector("body");
|
|
6679
6750
|
if (!head && !body) return html;
|
|
6751
|
+
const liveStyles = [...document2.querySelectorAll("style")].filter(isAlwaysAppliedStyle);
|
|
6752
|
+
const dedupedStyles = dedupeFontFaceRules(liveStyles.map((el) => el.textContent || ""));
|
|
6753
|
+
liveStyles.forEach((el, i) => {
|
|
6754
|
+
el.textContent = dedupedStyles[i] ?? "";
|
|
6755
|
+
});
|
|
6680
6756
|
const styleEls = head ? Array.from(head.querySelectorAll("style")) : [];
|
|
6681
6757
|
const importRe = /@import\s+url\([^)]*\)\s*;|@import\s+["'][^"']+["']\s*;/gi;
|
|
6682
6758
|
for (const run of styleEls.length > 1 ? headStyleRuns(styleEls) : []) {
|
|
@@ -7516,9 +7592,10 @@ async function discoverMediaFromBrowser(page) {
|
|
|
7516
7592
|
const tagName = isImage ? "image" : htmlEl.tagName.toLowerCase() === "video" ? "video" : "audio";
|
|
7517
7593
|
const id = htmlEl.getAttribute("data-hf-render-id") || htmlEl.id || (isImage ? autoImageIds.get(htmlEl) : void 0);
|
|
7518
7594
|
if (!id) return;
|
|
7519
|
-
const
|
|
7520
|
-
const
|
|
7521
|
-
const
|
|
7595
|
+
const resolvedSrc = htmlEl.currentSrc || htmlEl.src || htmlEl.getAttribute("src") || "";
|
|
7596
|
+
const withoutQueryAndHash = (url) => url.replace(/[?#][\s\S]*$/, "");
|
|
7597
|
+
const isPageItself = withoutQueryAndHash(resolvedSrc) === withoutQueryAndHash(location.href);
|
|
7598
|
+
const src = isPageItself ? "" : resolvedSrc;
|
|
7522
7599
|
const start = parseFloat(htmlEl.getAttribute("data-start") || "0");
|
|
7523
7600
|
const endRaw = htmlEl.getAttribute("data-end");
|
|
7524
7601
|
const durationRaw = htmlEl.getAttribute("data-duration");
|
|
@@ -9824,6 +9901,7 @@ async function runCaptureStage(input) {
|
|
|
9824
9901
|
interleaveFrames: input.interleaveFrames,
|
|
9825
9902
|
dedupPerfs,
|
|
9826
9903
|
pageErrorLines: workerPageErrors,
|
|
9904
|
+
openAttempt: input.openAttempt,
|
|
9827
9905
|
onProgress: (progress) => {
|
|
9828
9906
|
if (progress.latestWorkerPhase) {
|
|
9829
9907
|
if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
|
|
@@ -9895,6 +9973,11 @@ async function runSequentialDiskCapture(input, captureOptions, captureCfg, check
|
|
|
9895
9973
|
let transientRetriesUsed = 0;
|
|
9896
9974
|
let resumeFrom = 0;
|
|
9897
9975
|
while (true) {
|
|
9976
|
+
const attempt = input.openAttempt?.();
|
|
9977
|
+
const assertAttemptActive = () => {
|
|
9978
|
+
assertNotAborted();
|
|
9979
|
+
attempt?.throwIfAborted();
|
|
9980
|
+
};
|
|
9898
9981
|
const videoInjector = createRenderVideoFrameInjector();
|
|
9899
9982
|
const session = probeSession ?? await createCaptureSession5(
|
|
9900
9983
|
fileServer.url,
|
|
@@ -9910,10 +9993,10 @@ async function runSequentialDiskCapture(input, captureOptions, captureCfg, check
|
|
|
9910
9993
|
probeSession = null;
|
|
9911
9994
|
}
|
|
9912
9995
|
await ensureSessionReady(session);
|
|
9913
|
-
|
|
9996
|
+
assertAttemptActive();
|
|
9914
9997
|
lastBrowserConsole = session.browserConsoleBuffer;
|
|
9915
9998
|
await captureSessionFrames(session, {
|
|
9916
|
-
input,
|
|
9999
|
+
input: { ...input, assertNotAborted: assertAttemptActive },
|
|
9917
10000
|
rangeStart,
|
|
9918
10001
|
rangeEnd,
|
|
9919
10002
|
resumeFrom,
|
|
@@ -11773,7 +11856,17 @@ function captureAttemptMadeProgress(attemptTargetFrameCount, remainingFrameCount
|
|
|
11773
11856
|
function resetCaptureAttemptProgress(job) {
|
|
11774
11857
|
job.framesRendered = 0;
|
|
11775
11858
|
}
|
|
11859
|
+
var FIRST_FRAME_STALL_MS = 3e5;
|
|
11860
|
+
function frameStallRule(job) {
|
|
11861
|
+
const noProgressMs = resolveCaptureStallTimeoutMs();
|
|
11862
|
+
return {
|
|
11863
|
+
progress: () => job.framesRendered ?? 0,
|
|
11864
|
+
noProgressMs,
|
|
11865
|
+
firstProgressMs: Math.max(FIRST_FRAME_STALL_MS, noProgressMs)
|
|
11866
|
+
};
|
|
11867
|
+
}
|
|
11776
11868
|
function isRecoverableParallelCaptureError(error) {
|
|
11869
|
+
if (error instanceof RenderStageStalledError) return true;
|
|
11777
11870
|
const message = normalizeErrorMessage(error);
|
|
11778
11871
|
if (!message.includes("[Parallel] Capture failed")) return false;
|
|
11779
11872
|
const kind = classifyCaptureFailure3(error).kind;
|
|
@@ -11836,8 +11929,10 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11836
11929
|
)
|
|
11837
11930
|
];
|
|
11838
11931
|
options.dedupPerfs.length = 0;
|
|
11932
|
+
let attemptSignal = options.abortSignal;
|
|
11839
11933
|
try {
|
|
11840
11934
|
for (const tasks of batches) {
|
|
11935
|
+
attemptSignal = options.openAttempt?.(options.abortSignal) ?? options.abortSignal;
|
|
11841
11936
|
const capturedBeforeBatch = countCapturedFrames(
|
|
11842
11937
|
options.totalFrames,
|
|
11843
11938
|
options.framesDir,
|
|
@@ -11850,7 +11945,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11850
11945
|
tasks,
|
|
11851
11946
|
options.captureOptions,
|
|
11852
11947
|
options.createBeforeCaptureHook,
|
|
11853
|
-
|
|
11948
|
+
attemptSignal,
|
|
11854
11949
|
options.onProgress ? (progress) => {
|
|
11855
11950
|
options.onProgress?.({
|
|
11856
11951
|
...progress,
|
|
@@ -11898,7 +11993,8 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11898
11993
|
currentWorkers = nextWorkers;
|
|
11899
11994
|
missingRanges = remaining;
|
|
11900
11995
|
attempt++;
|
|
11901
|
-
} catch (
|
|
11996
|
+
} catch (caught) {
|
|
11997
|
+
const error = attemptSignal?.aborted && !options.abortSignal?.aborted ? attemptSignal.reason : caught;
|
|
11902
11998
|
const failure = classifyCaptureFailure3(error, { signal: options.abortSignal });
|
|
11903
11999
|
if (failure.kind === "cancelled") {
|
|
11904
12000
|
throw error;
|
|
@@ -13567,37 +13663,44 @@ async function executeRenderPipeline(input) {
|
|
|
13567
13663
|
observability,
|
|
13568
13664
|
"capture_hdr_layered",
|
|
13569
13665
|
captureStageObservationData({ hasHdrContent }),
|
|
13570
|
-
() =>
|
|
13571
|
-
|
|
13572
|
-
|
|
13573
|
-
|
|
13574
|
-
|
|
13575
|
-
|
|
13576
|
-
|
|
13577
|
-
|
|
13578
|
-
|
|
13579
|
-
|
|
13580
|
-
|
|
13581
|
-
|
|
13582
|
-
|
|
13583
|
-
|
|
13584
|
-
|
|
13585
|
-
|
|
13586
|
-
|
|
13587
|
-
|
|
13588
|
-
|
|
13589
|
-
|
|
13590
|
-
|
|
13591
|
-
|
|
13592
|
-
|
|
13593
|
-
|
|
13594
|
-
|
|
13595
|
-
|
|
13596
|
-
|
|
13597
|
-
|
|
13598
|
-
|
|
13599
|
-
|
|
13600
|
-
|
|
13666
|
+
(openAttempt) => {
|
|
13667
|
+
const attempt = openAttempt(executionSignal);
|
|
13668
|
+
return runCaptureHdrStage({
|
|
13669
|
+
job,
|
|
13670
|
+
cfg,
|
|
13671
|
+
plan: layeredPlan,
|
|
13672
|
+
log,
|
|
13673
|
+
projectDir,
|
|
13674
|
+
compiledDir,
|
|
13675
|
+
framesDir,
|
|
13676
|
+
videoOnlyPath,
|
|
13677
|
+
width,
|
|
13678
|
+
height,
|
|
13679
|
+
totalFrames,
|
|
13680
|
+
composition,
|
|
13681
|
+
hasHdrContent,
|
|
13682
|
+
effectiveHdr,
|
|
13683
|
+
nativeHdrVideoIds,
|
|
13684
|
+
nativeHdrImageIds,
|
|
13685
|
+
videoTransfers,
|
|
13686
|
+
imageTransfers,
|
|
13687
|
+
hdrImageSrcPaths,
|
|
13688
|
+
preset,
|
|
13689
|
+
effectiveQuality,
|
|
13690
|
+
effectiveBitrate,
|
|
13691
|
+
fileServer: activeFileServer,
|
|
13692
|
+
buildCaptureOptions,
|
|
13693
|
+
createRenderVideoFrameInjector,
|
|
13694
|
+
hdrDiagnostics,
|
|
13695
|
+
abortSignal: attempt,
|
|
13696
|
+
assertNotAborted: () => {
|
|
13697
|
+
assertNotAborted();
|
|
13698
|
+
attempt.throwIfAborted();
|
|
13699
|
+
},
|
|
13700
|
+
onProgress
|
|
13701
|
+
});
|
|
13702
|
+
},
|
|
13703
|
+
{ stall: frameStallRule(job) }
|
|
13601
13704
|
);
|
|
13602
13705
|
lastBrowserConsole = hdrRes.lastBrowserConsole;
|
|
13603
13706
|
layeredCaptureWarnings.push(...hdrRes.warnings);
|
|
@@ -13962,7 +14065,7 @@ async function executeRenderPipeline(input) {
|
|
|
13962
14065
|
observability,
|
|
13963
14066
|
"capture_disk",
|
|
13964
14067
|
captureStageObservationData({ needsAlpha: diskPlan.needsAlpha }),
|
|
13965
|
-
() => runCaptureStage({
|
|
14068
|
+
(openAttempt) => runCaptureStage({
|
|
13966
14069
|
fileServer: activeFileServer,
|
|
13967
14070
|
workDir,
|
|
13968
14071
|
framesDir,
|
|
@@ -13980,8 +14083,10 @@ async function executeRenderPipeline(input) {
|
|
|
13980
14083
|
assertNotAborted,
|
|
13981
14084
|
onProgress,
|
|
13982
14085
|
onFrameLanded: (frame) => overlap?.onFrameLanded(frame),
|
|
13983
|
-
interleaveFrames: overlap !== null
|
|
13984
|
-
|
|
14086
|
+
interleaveFrames: overlap !== null,
|
|
14087
|
+
openAttempt
|
|
14088
|
+
}),
|
|
14089
|
+
{ stall: frameStallRule(job) }
|
|
13985
14090
|
);
|
|
13986
14091
|
let captureRes;
|
|
13987
14092
|
try {
|
|
@@ -17639,6 +17744,7 @@ import {
|
|
|
17639
17744
|
} from "@hyperframes/core";
|
|
17640
17745
|
import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options";
|
|
17641
17746
|
var DEFAULT_SERVER_FPS = { num: 30, den: 1 };
|
|
17747
|
+
var SSE_KEEPALIVE_MS = 3e4;
|
|
17642
17748
|
var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
17643
17749
|
"ASSET_MEDIA_TYPE_MISMATCH",
|
|
17644
17750
|
"NOT_MEDIA_PAYLOAD",
|
|
@@ -17646,7 +17752,8 @@ var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
|
17646
17752
|
"VIDEO_SOURCE_UNRENDERABLE",
|
|
17647
17753
|
"VIDEO_EXTRACTION_FAILED",
|
|
17648
17754
|
"ENCODER_INTERRUPTED",
|
|
17649
|
-
"RENDER_TOO_SLOW"
|
|
17755
|
+
"RENDER_TOO_SLOW",
|
|
17756
|
+
"RENDER_STAGE_STALLED"
|
|
17650
17757
|
]);
|
|
17651
17758
|
function extractSafeRenderErrorCode(error) {
|
|
17652
17759
|
if (typeof error !== "object" || error === null || !("code" in error)) return void 0;
|
|
@@ -18160,15 +18267,21 @@ function createRenderHandlers(options = {}) {
|
|
|
18160
18267
|
})
|
|
18161
18268
|
});
|
|
18162
18269
|
}
|
|
18270
|
+
const reportProgress = createSseProgressReporter(stream, requestId);
|
|
18271
|
+
const keepalive = setInterval(() => {
|
|
18272
|
+
Promise.resolve(reportProgress(job, job.currentStage)).catch(() => {
|
|
18273
|
+
});
|
|
18274
|
+
}, SSE_KEEPALIVE_MS);
|
|
18163
18275
|
const release = await renderSemaphore.acquire();
|
|
18164
18276
|
try {
|
|
18165
18277
|
await executeRenderJob(
|
|
18166
18278
|
job,
|
|
18167
18279
|
input.projectDir,
|
|
18168
18280
|
absoluteOutputPath,
|
|
18169
|
-
|
|
18281
|
+
reportProgress,
|
|
18170
18282
|
abortController.signal
|
|
18171
|
-
);
|
|
18283
|
+
).finally(release);
|
|
18284
|
+
clearInterval(keepalive);
|
|
18172
18285
|
const fileSize = outputFileSize(absoluteOutputPath);
|
|
18173
18286
|
const outputToken = store.register(absoluteOutputPath);
|
|
18174
18287
|
const outputUrl = `${outputUrlPrefix}/${outputToken}`;
|
|
@@ -18188,6 +18301,7 @@ function createRenderHandlers(options = {}) {
|
|
|
18188
18301
|
})
|
|
18189
18302
|
});
|
|
18190
18303
|
} catch (error) {
|
|
18304
|
+
clearInterval(keepalive);
|
|
18191
18305
|
await writeRenderStreamFailure({
|
|
18192
18306
|
error,
|
|
18193
18307
|
job,
|
|
@@ -18197,7 +18311,6 @@ function createRenderHandlers(options = {}) {
|
|
|
18197
18311
|
log
|
|
18198
18312
|
});
|
|
18199
18313
|
} finally {
|
|
18200
|
-
release();
|
|
18201
18314
|
c.req.raw.signal.removeEventListener("abort", onRequestAbort);
|
|
18202
18315
|
cleanupTempDir(cleanupProjectDir, log);
|
|
18203
18316
|
}
|
|
@@ -20014,29 +20127,36 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
|
|
|
20014
20127
|
const captureSession = session;
|
|
20015
20128
|
session = null;
|
|
20016
20129
|
overlap.current = canEncodeWhileCapturing(encodeInput, chunkWorkerCount, cpus3().length) ? encodeWhileCapturing(encodeInput, framesInChunk) : null;
|
|
20017
|
-
await
|
|
20018
|
-
|
|
20019
|
-
|
|
20020
|
-
|
|
20021
|
-
|
|
20022
|
-
|
|
20023
|
-
|
|
20024
|
-
|
|
20025
|
-
|
|
20026
|
-
|
|
20027
|
-
|
|
20028
|
-
|
|
20029
|
-
|
|
20030
|
-
|
|
20031
|
-
|
|
20032
|
-
|
|
20033
|
-
|
|
20034
|
-
|
|
20035
|
-
|
|
20036
|
-
|
|
20037
|
-
|
|
20038
|
-
|
|
20039
|
-
|
|
20130
|
+
await observeRenderStage(
|
|
20131
|
+
new RenderObservabilityRecorder({ pipelineStartMs: start, log }),
|
|
20132
|
+
"capture_chunk",
|
|
20133
|
+
{ chunkIndex },
|
|
20134
|
+
(openAttempt) => runCaptureStage({
|
|
20135
|
+
fileServer,
|
|
20136
|
+
workDir,
|
|
20137
|
+
framesDir,
|
|
20138
|
+
job,
|
|
20139
|
+
totalFrames: framesInChunk,
|
|
20140
|
+
cfg: captureCfg,
|
|
20141
|
+
plan: capturePlan,
|
|
20142
|
+
log,
|
|
20143
|
+
probeSession: captureSession,
|
|
20144
|
+
captureAttempts,
|
|
20145
|
+
// This sink records each worker's effective capture mode so a
|
|
20146
|
+
// fallback remains observable to adapters and smoke tests.
|
|
20147
|
+
dedupPerfs: capturePerfs,
|
|
20148
|
+
buildCaptureOptions: () => captureOptions,
|
|
20149
|
+
createRenderVideoFrameInjector: createChunkVideoFrameInjector,
|
|
20150
|
+
abortSignal: void 0,
|
|
20151
|
+
assertNotAborted: () => {
|
|
20152
|
+
},
|
|
20153
|
+
frameRange: range,
|
|
20154
|
+
onFrameLanded: (frame) => overlap.current?.onFrameLanded(frame),
|
|
20155
|
+
interleaveFrames: interleavesCapture(overlap.current !== null, options),
|
|
20156
|
+
openAttempt
|
|
20157
|
+
}),
|
|
20158
|
+
{ stall: frameStallRule(job) }
|
|
20159
|
+
).catch(async (captureError) => {
|
|
20040
20160
|
await overlap.current?.abort();
|
|
20041
20161
|
overlap.current = null;
|
|
20042
20162
|
throw captureError;
|