@hyperframes/producer 0.8.191 → 0.8.193

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.
@@ -1384,18 +1384,9 @@ var RenderExecutionContext = class {
1384
1384
  };
1385
1385
 
1386
1386
  // src/services/render/artifactTransaction.ts
1387
- import {
1388
- closeSync as closeSync2,
1389
- existsSync as existsSync5,
1390
- fstatSync as fstatSync2,
1391
- openSync as openSync2,
1392
- readSync,
1393
- readdirSync as readdirSync2,
1394
- renameSync,
1395
- rmSync as rmSync3
1396
- } from "node:fs";
1387
+ import { closeSync as closeSync2, existsSync as existsSync5, fstatSync as fstatSync2, openSync as openSync2, readSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
1397
1388
  import { basename as basename3, dirname as dirname4, extname as extname2, join as join5, resolve as resolve4 } from "node:path";
1398
- import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST } from "@hyperframes/engine";
1389
+ import { HLS_MASTER_PLAYLIST, HLS_VIDEO_PLAYLIST, renameWithRetry } from "@hyperframes/engine";
1399
1390
 
1400
1391
  // src/utils/ffprobe.ts
1401
1392
  import {
@@ -1514,7 +1505,7 @@ function createRenderWorkDir(prefix, outputDir) {
1514
1505
  // src/services/render/artifactTransaction.ts
1515
1506
  var defaultFileSystem = {
1516
1507
  existsSync: existsSync5,
1517
- renameSync,
1508
+ renameSync: (source, destination) => renameWithRetry(source, destination),
1518
1509
  rmSync: rmSync3
1519
1510
  };
1520
1511
  async function defaultArtifactDurationProbe(path) {
@@ -1913,58 +1904,10 @@ import {
1913
1904
 
1914
1905
  // src/services/render/captureStageError.ts
1915
1906
  import { CaptureFailure, classifyCaptureFailure } from "@hyperframes/engine";
1916
- var CaptureStageError = class extends CaptureFailure {
1917
- browserConsole;
1918
- constructor(input) {
1919
- const classified = classifyCaptureFailure(input.cause);
1920
- super({
1921
- kind: classified.kind,
1922
- message: normalizeErrorMessage(input.cause),
1923
- cause: input.cause,
1924
- workerDiagnostics: classified.workerDiagnostics
1925
- });
1926
- this.name = "CaptureStageError";
1927
- this.browserConsole = input.browserConsole.slice();
1928
- if (input.cause instanceof Error && input.cause.stack) {
1929
- this.stack = input.cause.stack;
1930
- }
1931
- }
1932
- };
1933
- function wrapCaptureStageError(error, browserConsole) {
1934
- if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
1935
- return new CaptureStageError({ cause: error, browserConsole });
1936
- }
1937
- function getCaptureStageBrowserConsole(error) {
1938
- if (error instanceof CaptureStageError) return error.browserConsole;
1939
- return [];
1940
- }
1941
-
1942
- // src/services/render/stages/captureHdrFrameShared.ts
1943
- import {
1944
- applyDomLayerMask as applyDomLayerMask2,
1945
- blitRgba8OverRgb48le as blitRgba8OverRgb48le2,
1946
- captureAlphaPng as captureAlphaPng2,
1947
- decodePng as decodePng2,
1948
- queryElementStacking as queryElementStacking2,
1949
- removeDomLayerMask as removeDomLayerMask2
1950
- } from "@hyperframes/engine";
1951
1907
 
1952
- // src/services/hdrCompositor.ts
1953
- import { readSync as readSync2, closeSync as closeSync3 } from "fs";
1954
- import { join as join6 } from "path";
1955
- import {
1956
- captureAlphaPng,
1957
- applyDomLayerMask,
1958
- removeDomLayerMask,
1959
- decodePng,
1960
- blitRgba8OverRgb48le,
1961
- blitRgb48leRegion,
1962
- groupIntoLayers,
1963
- blitRgb48leAffine,
1964
- parseTransformMatrix,
1965
- convertTransfer,
1966
- isVideoHiddenBeforeStreamStart
1967
- } from "@hyperframes/engine";
1908
+ // src/services/render/perfSummary.ts
1909
+ import { arch, cpus, platform, totalmem } from "node:os";
1910
+ import { fpsToNumber as fpsToNumber3 } from "@hyperframes/core";
1968
1911
 
1969
1912
  // src/services/render/hdrPerf.ts
1970
1913
  function createHdrPerfCollector() {
@@ -2070,7 +2013,280 @@ function finalizeHdrPerf(perf) {
2070
2013
  };
2071
2014
  }
2072
2015
 
2016
+ // src/services/render/perfSummary.ts
2017
+ function worstSubTimelineWaitOutcome(perfs) {
2018
+ const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
2019
+ if (outcomes.length === 0) return void 0;
2020
+ if (outcomes.includes("script_failure")) return "script_failure";
2021
+ if (outcomes.includes("timeout")) return "timeout";
2022
+ return "ready";
2023
+ }
2024
+ function pushWorkerDedupPerfs(results, sink) {
2025
+ for (const r of results) {
2026
+ if (r.perf) sink.push(r.perf);
2027
+ }
2028
+ }
2029
+ function pushWorkerPageErrors(results, sink) {
2030
+ for (const result of results) {
2031
+ for (const line of result.pageErrorLines ?? []) if (!sink.includes(line)) sink.push(line);
2032
+ }
2033
+ }
2034
+ function withWorkerPageErrors(consoleLines, workerLines) {
2035
+ return [...consoleLines, ...workerLines.filter((line) => !consoleLines.includes(line))];
2036
+ }
2037
+ function roundDb(value) {
2038
+ return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
2039
+ }
2040
+ function aggregateDrawElement(perfs, de) {
2041
+ if (perfs.length === 0) return void 0;
2042
+ const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
2043
+ const gateReasons = [
2044
+ ...new Set(
2045
+ [de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
2046
+ )
2047
+ ].sort();
2048
+ const gpuRenderers = [
2049
+ ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
2050
+ ].sort();
2051
+ const drain = de.drainStats;
2052
+ return {
2053
+ mode: modes.join("|") || "unknown",
2054
+ compileGate: de.compileGate,
2055
+ clampReason: de.clampReason,
2056
+ workerInversion: de.workerInversion ?? "none",
2057
+ preInversionWorkers: de.preInversionWorkers,
2058
+ compositionElementCount: de.compositionElementCount,
2059
+ compositionElementCountSource: de.compositionElementCountSource,
2060
+ compositionElementTags: de.compositionElementTags,
2061
+ arollVideoCount: de.arollVideoCount,
2062
+ heygenVideoCount: de.heygenVideoCount,
2063
+ adaptersUsed: de.adaptersUsed,
2064
+ audioCount: de.audioCount,
2065
+ imageCount: de.imageCount,
2066
+ subCompositionCount: de.subCompositionCount,
2067
+ audioGroupCount: de.audioGroupCount,
2068
+ colorGradingCount: de.colorGradingCount,
2069
+ hasLut: de.hasLut,
2070
+ rootBodyMismatch: de.rootBodyMismatch,
2071
+ rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
2072
+ vfxHostCount: de.vfxHostCount,
2073
+ vfxCapture: de.vfxCapture,
2074
+ vfxTypes: de.vfxTypes,
2075
+ shortBand: de.shortBand,
2076
+ parallelRouter: de.parallelRouter ?? "none",
2077
+ preRouterWorkers: de.preRouterWorkers,
2078
+ gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
2079
+ gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
2080
+ workerEncode: perfs.some((p) => p.deWorkerEncode),
2081
+ verifyArmed: perfs.reduce((sum, p) => sum + (p.deVerifyArmed ?? 0), 0),
2082
+ verifyChecked: drain?.verifyChecked ?? 0,
2083
+ verifyMinDb: roundDb(drain?.verifyMinDb),
2084
+ verifyInitMs: perfs.reduce((sum, p) => sum + (p.deVerifyInitMs ?? 0), 0),
2085
+ selfVerifyFallback: de.selfVerifyFallback,
2086
+ fallbackReason: de.fallbackReason,
2087
+ fallbackFailedDb: roundDb(de.fallbackFailedDb),
2088
+ fallbackFrameIndex: de.fallbackFrameIndex,
2089
+ fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
2090
+ blankSuspects: drain?.blankSuspects ?? 0,
2091
+ blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
2092
+ blankRecaptures: drain?.blankRecaptures ?? 0,
2093
+ boundaryFrames: perfs.reduce((sum, p) => sum + (p.deBoundaryFrames ?? 0), 0),
2094
+ ncprFallbacks: perfs.reduce((sum, p) => sum + (p.deNcprFallbacks ?? 0), 0),
2095
+ frameTimeouts: perfs.reduce((sum, p) => sum + (p.deFrameTimeouts ?? 0), 0)
2096
+ };
2097
+ }
2098
+ function maxDefined(values) {
2099
+ const present = values.filter((v) => typeof v === "number");
2100
+ return present.length > 0 ? Math.max(...present) : void 0;
2101
+ }
2102
+ function aggregateChromeMemory(perfs) {
2103
+ const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
2104
+ if (sampled.length === 0) return void 0;
2105
+ return {
2106
+ browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
2107
+ rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
2108
+ // Last samples are per session; summing approximates the whole fleet's
2109
+ // footprint at the end of capture (workers run concurrently).
2110
+ rssLastMb: sampled.reduce((sum, p) => sum + (p.chromeRssLastMb ?? 0), 0),
2111
+ gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
2112
+ samples: sampled.reduce((sum, p) => sum + (p.chromeMemorySamples ?? 0), 0)
2113
+ };
2114
+ }
2115
+ function aggregateDedup(perfs) {
2116
+ if (perfs.length === 0) return void 0;
2117
+ const armed = perfs.some((p) => p.staticDedupArmed);
2118
+ const skipReasons = armed ? [] : [
2119
+ ...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
2120
+ ].sort();
2121
+ const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
2122
+ const verificationOutcomes = [
2123
+ ...new Set(
2124
+ verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
2125
+ )
2126
+ ].sort();
2127
+ return {
2128
+ enabled: perfs.some((p) => p.staticDedupEnabled),
2129
+ armed,
2130
+ predictedFrames: perfs.reduce((sum, p) => sum + (p.staticDedupPredicted ?? 0), 0),
2131
+ reusedFrames: perfs.reduce((sum, p) => sum + (p.staticDedupReused ?? 0), 0),
2132
+ skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
2133
+ ...verificationPerfs.length === 0 ? {} : {
2134
+ verifiedFrames: verificationPerfs.reduce(
2135
+ (sum, perf) => sum + (perf.staticDedupVerified ?? 0),
2136
+ 0
2137
+ ),
2138
+ verificationOutcomes,
2139
+ plannedRuns: verificationPerfs.reduce(
2140
+ (sum, perf) => sum + (perf.staticDedupVerificationPlannedRuns ?? 0),
2141
+ 0
2142
+ ),
2143
+ completedRuns: verificationPerfs.reduce(
2144
+ (sum, perf) => sum + (perf.staticDedupVerificationCompletedRuns ?? 0),
2145
+ 0
2146
+ ),
2147
+ screenshots: verificationPerfs.reduce(
2148
+ (sum, perf) => sum + (perf.staticDedupVerificationScreenshots ?? 0),
2149
+ 0
2150
+ ),
2151
+ seeks: verificationPerfs.reduce(
2152
+ (sum, perf) => sum + (perf.staticDedupVerificationSeeks ?? 0),
2153
+ 0
2154
+ ),
2155
+ comparisons: verificationPerfs.reduce(
2156
+ (sum, perf) => sum + (perf.staticDedupVerificationComparisons ?? 0),
2157
+ 0
2158
+ ),
2159
+ verificationElapsedMs: verificationPerfs.reduce(
2160
+ (sum, perf) => sum + (perf.staticDedupVerificationElapsedMs ?? 0),
2161
+ 0
2162
+ )
2163
+ }
2164
+ };
2165
+ }
2166
+ function aggregateBeginFrameReuse(perfs) {
2167
+ const noDamageFrames = perfs.reduce((sum, p) => sum + (p.beginFrameNoDamage ?? 0), 0);
2168
+ const hasDamageFrames = perfs.reduce((sum, p) => sum + (p.beginFrameHasDamage ?? 0), 0);
2169
+ if (noDamageFrames + hasDamageFrames === 0) return void 0;
2170
+ return { noDamageFrames, hasDamageFrames };
2171
+ }
2172
+ function buildRenderPerfSummary(input) {
2173
+ return {
2174
+ renderId: input.job.id,
2175
+ totalElapsedMs: input.totalElapsedMs,
2176
+ // RenderPerfSummary surfaces fps as a decimal because it lands in JSON
2177
+ // payloads (CLI telemetry, regression-harness reports) where a single
2178
+ // number is friendlier than `{num,den}`. Callers needing the rational
2179
+ // back can read `job.config.fps`.
2180
+ fps: fpsToNumber3(input.job.config.fps),
2181
+ quality: input.job.config.quality,
2182
+ workers: input.workerCount,
2183
+ workerSizing: input.workerSizing,
2184
+ chunkedEncode: input.enableChunkedEncode,
2185
+ chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
2186
+ compositionDurationSeconds: input.compositionDurationSeconds,
2187
+ totalFrames: input.totalFrames,
2188
+ resolution: { width: input.outputWidth, height: input.outputHeight },
2189
+ videoCount: input.videoCount,
2190
+ audioCount: input.audioCount,
2191
+ stages: input.perfStages,
2192
+ videoExtractBreakdown: input.videoExtractBreakdown,
2193
+ tmpPeakBytes: input.tmpPeakBytes,
2194
+ captureCalibration: input.captureCalibration ? {
2195
+ sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
2196
+ p95Ms: input.captureCalibration.estimate.p95Ms,
2197
+ multiplier: input.captureCalibration.estimate.multiplier,
2198
+ reasons: input.captureCalibration.estimate.reasons
2199
+ } : void 0,
2200
+ captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
2201
+ hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
2202
+ hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
2203
+ observability: input.observability,
2204
+ captureAvgMs: input.totalFrames > 0 ? Math.round(
2205
+ (input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
2206
+ ) : void 0,
2207
+ subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
2208
+ captureP50Ms: (() => {
2209
+ const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
2210
+ if (withSamples.length === 0) return void 0;
2211
+ return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
2212
+ })(),
2213
+ peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
2214
+ peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
2215
+ staticDedup: aggregateDedup(input.dedupPerfs),
2216
+ chromeMemory: aggregateChromeMemory(input.dedupPerfs),
2217
+ beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
2218
+ drawElement: aggregateDrawElement(
2219
+ input.dedupPerfs,
2220
+ input.drawElement ?? { selfVerifyFallback: false }
2221
+ ),
2222
+ host: {
2223
+ platform: platform(),
2224
+ arch: arch(),
2225
+ cpuCount: cpus().length,
2226
+ totalMemMb: Math.round(totalmem() / (1024 * 1024)),
2227
+ nodeVersion: process.version,
2228
+ gpuDisabled: input.gpuDisabled
2229
+ }
2230
+ };
2231
+ }
2232
+
2233
+ // src/services/render/captureStageError.ts
2234
+ var CaptureStageError = class extends CaptureFailure {
2235
+ browserConsole;
2236
+ constructor(input) {
2237
+ const classified = classifyCaptureFailure(input.cause);
2238
+ super({
2239
+ kind: classified.kind,
2240
+ message: normalizeErrorMessage(input.cause),
2241
+ cause: input.cause,
2242
+ workerDiagnostics: classified.workerDiagnostics,
2243
+ pageErrorLines: classified.pageErrorLines
2244
+ });
2245
+ this.name = "CaptureStageError";
2246
+ this.browserConsole = withWorkerPageErrors(input.browserConsole, [
2247
+ ...classified.pageErrorLines
2248
+ ]);
2249
+ if (input.cause instanceof Error && input.cause.stack) {
2250
+ this.stack = input.cause.stack;
2251
+ }
2252
+ }
2253
+ };
2254
+ function wrapCaptureStageError(error, browserConsole) {
2255
+ if (error instanceof CaptureStageError || error instanceof EncoderInterruptedError) return error;
2256
+ return new CaptureStageError({ cause: error, browserConsole });
2257
+ }
2258
+ function getCaptureStageBrowserConsole(error) {
2259
+ if (error instanceof CaptureStageError) return error.browserConsole;
2260
+ if (error instanceof CaptureFailure) return [...error.pageErrorLines];
2261
+ return [];
2262
+ }
2263
+
2264
+ // src/services/render/stages/captureHdrFrameShared.ts
2265
+ import {
2266
+ applyDomLayerMask as applyDomLayerMask2,
2267
+ blitRgba8OverRgb48le as blitRgba8OverRgb48le2,
2268
+ captureAlphaPng as captureAlphaPng2,
2269
+ decodePng as decodePng2,
2270
+ queryElementStacking as queryElementStacking2,
2271
+ removeDomLayerMask as removeDomLayerMask2
2272
+ } from "@hyperframes/engine";
2273
+
2073
2274
  // src/services/hdrCompositor.ts
2275
+ import { readSync as readSync2, closeSync as closeSync3 } from "fs";
2276
+ import { join as join6 } from "path";
2277
+ import {
2278
+ captureAlphaPng,
2279
+ applyDomLayerMask,
2280
+ removeDomLayerMask,
2281
+ decodePng,
2282
+ blitRgba8OverRgb48le,
2283
+ blitRgb48leRegion,
2284
+ groupIntoLayers,
2285
+ blitRgb48leAffine,
2286
+ parseTransformMatrix,
2287
+ convertTransfer,
2288
+ isVideoHiddenBeforeStreamStart
2289
+ } from "@hyperframes/engine";
2074
2290
  function countNonZeroAlpha(rgba) {
2075
2291
  let n = 0;
2076
2292
  for (let p = 3; p < rgba.length; p += 4) {
@@ -2537,7 +2753,7 @@ import {
2537
2753
  resolveVideoExtractionWindow,
2538
2754
  runFfmpeg
2539
2755
  } from "@hyperframes/engine";
2540
- import { fpsToFfmpegArg, fpsToNumber as fpsToNumber3 } from "@hyperframes/core";
2756
+ import { fpsToFfmpegArg, fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
2541
2757
  var NO_FOLLOW_FLAG = constants2.O_NOFOLLOW ?? 0;
2542
2758
  function tempDirSafePrefix(id) {
2543
2759
  const safe = id.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 80);
@@ -2773,7 +2989,7 @@ async function extractHdrVideoFrames(args) {
2773
2989
  const { estimatedBytes, budgetBytes } = assertHdrExtractionDiskHeadroom(
2774
2990
  framesDir,
2775
2991
  plannedVideos,
2776
- fpsToNumber3(job.config.fps),
2992
+ fpsToNumber4(job.config.fps),
2777
2993
  log
2778
2994
  );
2779
2995
  const releaseReservation = reserveHdrExtractionBytes(estimatedBytes, budgetBytes);
@@ -3226,222 +3442,12 @@ import {
3226
3442
  prepareCaptureSessionForReuse,
3227
3443
  spawnStreamingEncoder
3228
3444
  } from "@hyperframes/engine";
3229
-
3230
- // src/services/render/perfSummary.ts
3231
- import { arch, cpus, platform, totalmem } from "node:os";
3232
- import { fpsToNumber as fpsToNumber4 } from "@hyperframes/core";
3233
- function worstSubTimelineWaitOutcome(perfs) {
3234
- const outcomes = perfs.map((p) => p.subTimelineWaitOutcome).filter((o) => !!o);
3235
- if (outcomes.length === 0) return void 0;
3236
- if (outcomes.includes("script_failure")) return "script_failure";
3237
- if (outcomes.includes("timeout")) return "timeout";
3238
- return "ready";
3239
- }
3240
- function pushWorkerDedupPerfs(results, sink) {
3241
- for (const r of results) {
3242
- if (r.perf) sink.push(r.perf);
3243
- }
3244
- }
3245
- function roundDb(value) {
3246
- return value === void 0 ? void 0 : Math.round(Math.min(value, 999) * 10) / 10;
3247
- }
3248
- function aggregateDrawElement(perfs, de) {
3249
- if (perfs.length === 0) return void 0;
3250
- const modes = [...new Set(perfs.map((p) => p.captureMode).filter(Boolean))].sort();
3251
- const gateReasons = [
3252
- ...new Set(
3253
- [de.probeGateReason, ...perfs.map((p) => p.deGateReason)].filter((r) => !!r)
3254
- )
3255
- ].sort();
3256
- const gpuRenderers = [
3257
- ...new Set(perfs.map((p) => p.gpuRenderer).filter((r) => !!r))
3258
- ].sort();
3259
- const drain = de.drainStats;
3260
- return {
3261
- mode: modes.join("|") || "unknown",
3262
- compileGate: de.compileGate,
3263
- clampReason: de.clampReason,
3264
- workerInversion: de.workerInversion ?? "none",
3265
- preInversionWorkers: de.preInversionWorkers,
3266
- compositionElementCount: de.compositionElementCount,
3267
- compositionElementCountSource: de.compositionElementCountSource,
3268
- compositionElementTags: de.compositionElementTags,
3269
- arollVideoCount: de.arollVideoCount,
3270
- heygenVideoCount: de.heygenVideoCount,
3271
- adaptersUsed: de.adaptersUsed,
3272
- audioCount: de.audioCount,
3273
- imageCount: de.imageCount,
3274
- subCompositionCount: de.subCompositionCount,
3275
- audioGroupCount: de.audioGroupCount,
3276
- colorGradingCount: de.colorGradingCount,
3277
- hasLut: de.hasLut,
3278
- rootBodyMismatch: de.rootBodyMismatch,
3279
- rootBodyDeltaPxBucket: de.rootBodyDeltaPxBucket,
3280
- vfxHostCount: de.vfxHostCount,
3281
- vfxCapture: de.vfxCapture,
3282
- vfxTypes: de.vfxTypes,
3283
- shortBand: de.shortBand,
3284
- parallelRouter: de.parallelRouter ?? "none",
3285
- preRouterWorkers: de.preRouterWorkers,
3286
- gateReason: gateReasons.length > 0 ? gateReasons.join("|") : void 0,
3287
- gpuRenderer: gpuRenderers.length > 0 ? gpuRenderers.join("|") : void 0,
3288
- workerEncode: perfs.some((p) => p.deWorkerEncode),
3289
- verifyArmed: perfs.reduce((sum, p) => sum + (p.deVerifyArmed ?? 0), 0),
3290
- verifyChecked: drain?.verifyChecked ?? 0,
3291
- verifyMinDb: roundDb(drain?.verifyMinDb),
3292
- verifyInitMs: perfs.reduce((sum, p) => sum + (p.deVerifyInitMs ?? 0), 0),
3293
- selfVerifyFallback: de.selfVerifyFallback,
3294
- fallbackReason: de.fallbackReason,
3295
- fallbackFailedDb: roundDb(de.fallbackFailedDb),
3296
- fallbackFrameIndex: de.fallbackFrameIndex,
3297
- fallbackThresholdDb: roundDb(de.fallbackThresholdDb),
3298
- blankSuspects: drain?.blankSuspects ?? 0,
3299
- blankDeterministicAccepts: drain?.blankDeterministicAccepts ?? 0,
3300
- blankRecaptures: drain?.blankRecaptures ?? 0,
3301
- boundaryFrames: perfs.reduce((sum, p) => sum + (p.deBoundaryFrames ?? 0), 0),
3302
- ncprFallbacks: perfs.reduce((sum, p) => sum + (p.deNcprFallbacks ?? 0), 0),
3303
- frameTimeouts: perfs.reduce((sum, p) => sum + (p.deFrameTimeouts ?? 0), 0)
3304
- };
3305
- }
3306
- function maxDefined(values) {
3307
- const present = values.filter((v) => typeof v === "number");
3308
- return present.length > 0 ? Math.max(...present) : void 0;
3309
- }
3310
- function aggregateChromeMemory(perfs) {
3311
- const sampled = perfs.filter((p) => (p.chromeMemorySamples ?? 0) > 0);
3312
- if (sampled.length === 0) return void 0;
3313
- return {
3314
- browserRssPeakMb: maxDefined(sampled.map((p) => p.chromeBrowserRssPeakMb)),
3315
- rendererRssPeakMb: maxDefined(sampled.map((p) => p.chromeRendererRssPeakMb)),
3316
- // Last samples are per session; summing approximates the whole fleet's
3317
- // footprint at the end of capture (workers run concurrently).
3318
- rssLastMb: sampled.reduce((sum, p) => sum + (p.chromeRssLastMb ?? 0), 0),
3319
- gpuProcessSeenLastSample: sampled.some((p) => p.chromeGpuProcessSeenLastSample === true),
3320
- samples: sampled.reduce((sum, p) => sum + (p.chromeMemorySamples ?? 0), 0)
3321
- };
3322
- }
3323
- function aggregateDedup(perfs) {
3324
- if (perfs.length === 0) return void 0;
3325
- const armed = perfs.some((p) => p.staticDedupArmed);
3326
- const skipReasons = armed ? [] : [
3327
- ...new Set(perfs.map((p) => p.staticDedupSkipReason).filter((r) => !!r))
3328
- ].sort();
3329
- const verificationPerfs = perfs.filter((perf) => perf.staticDedupVerificationOutcome);
3330
- const verificationOutcomes = [
3331
- ...new Set(
3332
- verificationPerfs.map((perf) => perf.staticDedupVerificationOutcome).filter((outcome) => outcome != null)
3333
- )
3334
- ].sort();
3335
- return {
3336
- enabled: perfs.some((p) => p.staticDedupEnabled),
3337
- armed,
3338
- predictedFrames: perfs.reduce((sum, p) => sum + (p.staticDedupPredicted ?? 0), 0),
3339
- reusedFrames: perfs.reduce((sum, p) => sum + (p.staticDedupReused ?? 0), 0),
3340
- skipReason: skipReasons.length > 0 ? skipReasons.join("|") : void 0,
3341
- ...verificationPerfs.length === 0 ? {} : {
3342
- verifiedFrames: verificationPerfs.reduce(
3343
- (sum, perf) => sum + (perf.staticDedupVerified ?? 0),
3344
- 0
3345
- ),
3346
- verificationOutcomes,
3347
- plannedRuns: verificationPerfs.reduce(
3348
- (sum, perf) => sum + (perf.staticDedupVerificationPlannedRuns ?? 0),
3349
- 0
3350
- ),
3351
- completedRuns: verificationPerfs.reduce(
3352
- (sum, perf) => sum + (perf.staticDedupVerificationCompletedRuns ?? 0),
3353
- 0
3354
- ),
3355
- screenshots: verificationPerfs.reduce(
3356
- (sum, perf) => sum + (perf.staticDedupVerificationScreenshots ?? 0),
3357
- 0
3358
- ),
3359
- seeks: verificationPerfs.reduce(
3360
- (sum, perf) => sum + (perf.staticDedupVerificationSeeks ?? 0),
3361
- 0
3362
- ),
3363
- comparisons: verificationPerfs.reduce(
3364
- (sum, perf) => sum + (perf.staticDedupVerificationComparisons ?? 0),
3365
- 0
3366
- ),
3367
- verificationElapsedMs: verificationPerfs.reduce(
3368
- (sum, perf) => sum + (perf.staticDedupVerificationElapsedMs ?? 0),
3369
- 0
3370
- )
3371
- }
3372
- };
3373
- }
3374
- function aggregateBeginFrameReuse(perfs) {
3375
- const noDamageFrames = perfs.reduce((sum, p) => sum + (p.beginFrameNoDamage ?? 0), 0);
3376
- const hasDamageFrames = perfs.reduce((sum, p) => sum + (p.beginFrameHasDamage ?? 0), 0);
3377
- if (noDamageFrames + hasDamageFrames === 0) return void 0;
3378
- return { noDamageFrames, hasDamageFrames };
3379
- }
3380
- function buildRenderPerfSummary(input) {
3381
- return {
3382
- renderId: input.job.id,
3383
- totalElapsedMs: input.totalElapsedMs,
3384
- // RenderPerfSummary surfaces fps as a decimal because it lands in JSON
3385
- // payloads (CLI telemetry, regression-harness reports) where a single
3386
- // number is friendlier than `{num,den}`. Callers needing the rational
3387
- // back can read `job.config.fps`.
3388
- fps: fpsToNumber4(input.job.config.fps),
3389
- quality: input.job.config.quality,
3390
- workers: input.workerCount,
3391
- workerSizing: input.workerSizing,
3392
- chunkedEncode: input.enableChunkedEncode,
3393
- chunkSizeFrames: input.enableChunkedEncode ? input.chunkedEncodeSize : null,
3394
- compositionDurationSeconds: input.compositionDurationSeconds,
3395
- totalFrames: input.totalFrames,
3396
- resolution: { width: input.outputWidth, height: input.outputHeight },
3397
- videoCount: input.videoCount,
3398
- audioCount: input.audioCount,
3399
- stages: input.perfStages,
3400
- videoExtractBreakdown: input.videoExtractBreakdown,
3401
- tmpPeakBytes: input.tmpPeakBytes,
3402
- captureCalibration: input.captureCalibration ? {
3403
- sampledFrames: input.captureCalibration.samples.map((sample) => sample.frameIndex),
3404
- p95Ms: input.captureCalibration.estimate.p95Ms,
3405
- multiplier: input.captureCalibration.estimate.multiplier,
3406
- reasons: input.captureCalibration.estimate.reasons
3407
- } : void 0,
3408
- captureAttempts: input.captureAttempts.length > 0 ? input.captureAttempts : void 0,
3409
- hdrDiagnostics: input.hdrDiagnostics.videoExtractionFailures > 0 || input.hdrDiagnostics.imageDecodeFailures > 0 ? { ...input.hdrDiagnostics } : void 0,
3410
- hdrPerf: input.hdrPerf ? finalizeHdrPerf(input.hdrPerf) : void 0,
3411
- observability: input.observability,
3412
- captureAvgMs: input.totalFrames > 0 ? Math.round(
3413
- (input.perfStages.captureFrameMs ?? input.perfStages.captureMs ?? 0) / input.totalFrames
3414
- ) : void 0,
3415
- subTimelineWait: worstSubTimelineWaitOutcome(input.dedupPerfs),
3416
- captureP50Ms: (() => {
3417
- const withSamples = input.dedupPerfs.filter((p) => (p.p50TotalMs ?? 0) > 0);
3418
- if (withSamples.length === 0) return void 0;
3419
- return withSamples.reduce((a, b) => b.frames > a.frames ? b : a).p50TotalMs;
3420
- })(),
3421
- peakRssMb: Math.round(input.peakRssBytes / (1024 * 1024)),
3422
- peakHeapUsedMb: Math.round(input.peakHeapUsedBytes / (1024 * 1024)),
3423
- staticDedup: aggregateDedup(input.dedupPerfs),
3424
- chromeMemory: aggregateChromeMemory(input.dedupPerfs),
3425
- beginFrameReuse: aggregateBeginFrameReuse(input.dedupPerfs),
3426
- drawElement: aggregateDrawElement(
3427
- input.dedupPerfs,
3428
- input.drawElement ?? { selfVerifyFallback: false }
3429
- ),
3430
- host: {
3431
- platform: platform(),
3432
- arch: arch(),
3433
- cpuCount: cpus().length,
3434
- totalMemMb: Math.round(totalmem() / (1024 * 1024)),
3435
- nodeVersion: process.version,
3436
- gpuDisabled: input.gpuDisabled
3437
- }
3438
- };
3439
- }
3440
-
3441
- // src/services/render/stages/captureStreamingStage.ts
3442
3445
  var DEFAULT_CAPTURE_STALL_MS = 6e4;
3443
3446
  var DE_STALL_POLL_MS = 5e3;
3444
3447
  var STALL_ADVICE = "Chrome stopped answering. If another app is keeping the GPU or CPU busy (a 3D render, a game, another export), let it finish or render one job at a time, then retry.";
3448
+ function poolFailure(error, probeSession) {
3449
+ return wrapCaptureStageError(error, probeSession?.browserConsoleBuffer ?? []);
3450
+ }
3445
3451
  var ParallelCaptureStallError = class extends Error {
3446
3452
  variant;
3447
3453
  constructor(args) {
@@ -3888,7 +3894,7 @@ async function runCaptureStreamingStage(input) {
3888
3894
  }
3889
3895
  );
3890
3896
  } catch (err) {
3891
- if (parallelDrainError) throw parallelDrainError;
3897
+ if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
3892
3898
  if (stalled && abortSignal?.aborted !== true) {
3893
3899
  throw new ParallelCaptureStallError({
3894
3900
  variant: "pool",
@@ -3899,13 +3905,15 @@ async function runCaptureStreamingStage(input) {
3899
3905
  phaseSummary: phaseSummary()
3900
3906
  });
3901
3907
  }
3902
- throw err;
3908
+ throw poolFailure(err, probeSession);
3903
3909
  } finally {
3904
3910
  clearInterval(stallTimer);
3905
3911
  abortSignal?.removeEventListener("abort", forwardParentAbort);
3906
3912
  }
3907
- if (parallelDrainError) throw parallelDrainError;
3913
+ if (parallelDrainError) throw poolFailure(parallelDrainError, probeSession);
3908
3914
  pushWorkerDedupPerfs(workerResults, dedupPerfs);
3915
+ const workerPageErrors = [];
3916
+ pushWorkerPageErrors(workerResults, workerPageErrors);
3909
3917
  if (parallelGuardRan) {
3910
3918
  deDrainStats = parallelStats;
3911
3919
  }
@@ -3915,6 +3923,7 @@ async function runCaptureStreamingStage(input) {
3915
3923
  await closeCaptureSession(probeSession);
3916
3924
  probeSession = null;
3917
3925
  }
3926
+ lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
3918
3927
  } else {
3919
3928
  const videoInjector = createRenderVideoFrameInjector();
3920
3929
  const session = probeSession ?? await createCaptureSession(
@@ -4361,8 +4370,9 @@ async function runCaptureSegmentedStage(input) {
4361
4370
 
4362
4371
  // src/services/render/segmentManifest.ts
4363
4372
  import { createHash as createHash2 } from "node:crypto";
4364
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync2, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
4373
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, statSync as statSync3, writeFileSync as writeFileSync3 } from "node:fs";
4365
4374
  import { join as join10 } from "node:path";
4375
+ import { renameWithRetry as renameWithRetry2 } from "@hyperframes/engine";
4366
4376
  var MANIFEST_FILENAME = "segments.json";
4367
4377
  function computeSegmentPlanHash(input) {
4368
4378
  const ordered = [
@@ -4418,7 +4428,7 @@ function writeSegmentManifest(segmentDir, manifest) {
4418
4428
  mkdirSync4(segmentDir, { recursive: true });
4419
4429
  const tmp = join10(segmentDir, `${MANIFEST_FILENAME}.${process.pid}.tmp`);
4420
4430
  writeFileSync3(tmp, JSON.stringify(manifest, null, 2), "utf-8");
4421
- renameSync2(tmp, join10(segmentDir, MANIFEST_FILENAME));
4431
+ renameWithRetry2(tmp, join10(segmentDir, MANIFEST_FILENAME));
4422
4432
  }
4423
4433
  async function probeSegmentFrameCount(path, probe) {
4424
4434
  try {
@@ -5470,19 +5480,11 @@ import {
5470
5480
 
5471
5481
  // src/services/animatedGifPrep.ts
5472
5482
  import { createHash as createHash4 } from "node:crypto";
5473
- import {
5474
- copyFileSync as copyFileSync2,
5475
- existsSync as existsSync8,
5476
- mkdirSync as mkdirSync5,
5477
- readFileSync as readFileSync5,
5478
- renameSync as renameSync3,
5479
- rmSync as rmSync6,
5480
- statSync as statSync4
5481
- } from "node:fs";
5483
+ import { copyFileSync as copyFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync6, statSync as statSync4 } from "node:fs";
5482
5484
  import { dirname as dirname5, isAbsolute as isAbsolute2, join as join12, resolve as resolve5 } from "node:path";
5483
5485
  import { parseHTML as parseHTML3 } from "linkedom";
5484
5486
  import { parseAnimatedGifMetadata } from "@hyperframes/core";
5485
- import { DEFAULT_VP9_CPU_USED, runFfmpeg as runFfmpeg2 } from "@hyperframes/engine";
5487
+ import { DEFAULT_VP9_CPU_USED, runFfmpeg as runFfmpeg2, renameWithRetry as renameWithRetry3 } from "@hyperframes/engine";
5486
5488
  var PREPARED_GIF_SUBDIR = "_animated_gif";
5487
5489
  var CACHE_SCHEMA = "hfgif-v1";
5488
5490
  function splitUrlSuffix(src) {
@@ -5661,7 +5663,7 @@ async function ensurePreparedWebm(input) {
5661
5663
  throw new Error("Animated GIF transcode produced an empty output");
5662
5664
  }
5663
5665
  if (!isUsableFile(input.cachePath)) {
5664
- renameSync3(tmpPath, input.cachePath);
5666
+ renameWithRetry3(tmpPath, input.cachePath);
5665
5667
  } else {
5666
5668
  rmSync6(tmpPath, { force: true });
5667
5669
  }
@@ -8068,6 +8070,10 @@ async function runProbeStage(input) {
8068
8070
  if (el.loop && !existing.loop) {
8069
8071
  existing.loop = true;
8070
8072
  }
8073
+ const audio = composition.audios.find(
8074
+ (audio2) => audio2.type === "video" && audio2.id === `${el.id}-audio`
8075
+ );
8076
+ if (audio) audio.loop = existing.loop;
8071
8077
  }
8072
8078
  } else if (!compiled.droppedMediaIds.has(el.id)) {
8073
8079
  composition.videos.push({
@@ -8105,6 +8111,7 @@ async function runProbeStage(input) {
8105
8111
  el,
8106
8112
  authoredStarts.get(el.id) ?? existing.mediaStart
8107
8113
  );
8114
+ existing.loop = el.loop;
8108
8115
  if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) > BROWSER_MEDIA_EPSILON) {
8109
8116
  existing.volume = el.volume;
8110
8117
  }
@@ -8116,6 +8123,7 @@ async function runProbeStage(input) {
8116
8123
  start: el.start,
8117
8124
  end: resolveBrowserMediaEnd(el.start, el.end, el.duration),
8118
8125
  mediaStart: el.mediaStart,
8126
+ loop: el.loop,
8119
8127
  layer: 0,
8120
8128
  volume: el.volume,
8121
8129
  type: "audio"
@@ -8339,6 +8347,10 @@ import {
8339
8347
  extractMediaMetadata as extractMediaMetadata3,
8340
8348
  isVideoPastTimelineEnd,
8341
8349
  isHdrColorSpace as isHdrColorSpace2,
8350
+ rebaseVideoToWindow,
8351
+ resolveFinalFrameExtractionWindow as resolveFinalFrameExtractionWindow2,
8352
+ resolveVideoExtractionWindow as resolveVideoExtractionWindow2,
8353
+ isHttpUrl as isHttpUrl2,
8342
8354
  resolveProjectRelativeSrc as resolveProjectRelativeSrc3,
8343
8355
  runVideoExtractionWithRetry,
8344
8356
  safeVideoExtractionSourceIdentity
@@ -8699,6 +8711,8 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
8699
8711
  start: video.start,
8700
8712
  end: video.end,
8701
8713
  mediaStart: video.mediaStart,
8714
+ playbackRate: video.playbackRate,
8715
+ loop: video.loop,
8702
8716
  layer: 0,
8703
8717
  volume: 1,
8704
8718
  type: "video"
@@ -8718,7 +8732,7 @@ import {
8718
8732
 
8719
8733
  // src/services/render/audioPadTrim.ts
8720
8734
  import { spawn } from "node:child_process";
8721
- import { mkdtempSync as mkdtempSync3, renameSync as renameSync4, rmSync as rmSync7 } from "node:fs";
8735
+ import { mkdtempSync as mkdtempSync3, rmSync as rmSync7 } from "node:fs";
8722
8736
  import { dirname as dirname7, join as join17 } from "node:path";
8723
8737
  import {
8724
8738
  DEFAULT_CONFIG,
@@ -8727,6 +8741,7 @@ import {
8727
8741
  extractAudioMetadata as extractAudioMetadata3,
8728
8742
  getFfprobeBinary,
8729
8743
  ManagedChildProcess,
8744
+ renameWithRetry as renameWithRetry4,
8730
8745
  runFfmpeg as runFfmpeg4,
8731
8746
  trackChildProcess
8732
8747
  } from "@hyperframes/engine";
@@ -8982,7 +8997,7 @@ async function enforceAacTruePeak(input) {
8982
8997
  }
8983
8998
  if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
8984
8999
  if (measuredPath !== correctedPath) return { success: true };
8985
- renameSync4(correctedPath, input.audioPath);
9000
+ renameWithRetry4(correctedPath, input.audioPath);
8986
9001
  return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
8987
9002
  }
8988
9003
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
@@ -9172,6 +9187,7 @@ async function runAudioStage(input) {
9172
9187
  const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
9173
9188
  let audioLoweredDb;
9174
9189
  if (result.hasAudio) {
9190
+ const guardStart = Date.now();
9175
9191
  const peak = await constrainAacTruePeak({
9176
9192
  audioPath: result.audioOutputPath,
9177
9193
  sourcePath: pcmSourcePath,
@@ -9181,6 +9197,7 @@ async function runAudioStage(input) {
9181
9197
  });
9182
9198
  if (!peak.success) throw encoderFailureError("Audio true-peak check failed", peak);
9183
9199
  audioLoweredDb = peak.loweredDb;
9200
+ input.log?.info("[audio] phases", { ...result.audioPhaseMs, guard: Date.now() - guardStart });
9184
9201
  }
9185
9202
  return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
9186
9203
  } finally {
@@ -9206,6 +9223,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9206
9223
  let hasAudio = false;
9207
9224
  let audioError;
9208
9225
  let audioFailures;
9226
+ let audioPhaseMs;
9209
9227
  if (audios.length > 0) {
9210
9228
  let audioResult;
9211
9229
  try {
@@ -9255,6 +9273,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9255
9273
  assertAudioNotInterrupted(audioResult.failures);
9256
9274
  hasAudio = audioResult.success;
9257
9275
  audioFailures = audioResult.failures;
9276
+ audioPhaseMs = audioResult.phaseMs;
9258
9277
  if (!hasAudio) {
9259
9278
  audioError = audioResult.error ?? "audio mix failed for an unknown reason";
9260
9279
  for (const failure of audioResult.failures ?? []) {
@@ -9273,7 +9292,8 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
9273
9292
  audioOutputPath,
9274
9293
  hasAudio,
9275
9294
  audioError,
9276
- audioFailures
9295
+ audioFailures,
9296
+ audioPhaseMs
9277
9297
  };
9278
9298
  }
9279
9299
 
@@ -9402,6 +9422,7 @@ async function runCaptureStage(input) {
9402
9422
  let { workerCount } = plan;
9403
9423
  const { forceScreenshot } = plan;
9404
9424
  let lastBrowserConsole = [];
9425
+ const workerPageErrors = [];
9405
9426
  let captureBeyondViewport = probeSession?.options.captureBeyondViewport;
9406
9427
  const captureCfg = cfg.forceScreenshot === forceScreenshot ? cfg : { ...cfg, forceScreenshot };
9407
9428
  if (frameRange !== void 0) {
@@ -9439,6 +9460,7 @@ async function runCaptureStage(input) {
9439
9460
  abortSignal,
9440
9461
  frameRangeStart: frameRange?.startFrame,
9441
9462
  dedupPerfs,
9463
+ pageErrorLines: workerPageErrors,
9442
9464
  onProgress: (progress) => {
9443
9465
  if (progress.latestWorkerPhase) {
9444
9466
  if (progress.capturedFrames === 0) reportWorkerStartup(job, progress, onProgress);
@@ -9459,6 +9481,11 @@ async function runCaptureStage(input) {
9459
9481
  },
9460
9482
  cfg: captureCfg,
9461
9483
  log
9484
+ }).catch((error) => {
9485
+ throw wrapCaptureStageError(
9486
+ error,
9487
+ withWorkerPageErrors(probeSession?.browserConsoleBuffer ?? [], workerPageErrors)
9488
+ );
9462
9489
  });
9463
9490
  captureAttempts.push(...attempts);
9464
9491
  const lastAttempt = attempts[attempts.length - 1];
@@ -9471,6 +9498,7 @@ async function runCaptureStage(input) {
9471
9498
  await closeCaptureSession5(probeSession);
9472
9499
  probeSession = null;
9473
9500
  }
9501
+ lastBrowserConsole = withWorkerPageErrors(lastBrowserConsole, workerPageErrors);
9474
9502
  } else {
9475
9503
  const sequential = await runSequentialDiskCapture(
9476
9504
  input,
@@ -10405,6 +10433,7 @@ async function runCaptureHdrStage(input) {
10405
10433
  width,
10406
10434
  height,
10407
10435
  codec: preset.codec,
10436
+ useGpu: job.config.useGpu,
10408
10437
  preset: preset.preset,
10409
10438
  quality: effectiveQuality,
10410
10439
  bitrate: effectiveBitrate,
@@ -10785,7 +10814,7 @@ function videoEncoderOptions(input, engineCfg) {
10785
10814
  };
10786
10815
  }
10787
10816
  function canEncodeWhileCapturing(input, workerCount, cpuCount) {
10788
- return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg" && input.lockGopForChunkConcat !== true;
10817
+ return workerCount > 1 && workerCount < cpuCount && !input.isPngSequence && !input.isGif && !input.enableChunkedEncode && input.preset.codec === "h264" && !input.preset.hdr && input.job.config.useGpu !== true && input.captureImageFormat === "jpeg";
10789
10818
  }
10790
10819
  async function readLandedFrame(workerPath, mergedPath) {
10791
10820
  if (workerPath !== void 0) {
@@ -11440,6 +11469,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
11440
11469
  options.cfg
11441
11470
  );
11442
11471
  pushWorkerDedupPerfs(workerResults, options.dedupPerfs);
11472
+ if (options.pageErrorLines) pushWorkerPageErrors(workerResults, options.pageErrorLines);
11443
11473
  } finally {
11444
11474
  await mergeWorkerFrames(attemptWorkDir, tasks, options.framesDir);
11445
11475
  }
@@ -14055,7 +14085,10 @@ import {
14055
14085
  import {
14056
14086
  getEncoderPreset as getEncoderPreset4,
14057
14087
  normalizeVp9CpuUsed,
14058
- resolveConfig as resolveConfig3
14088
+ resolveConfig as resolveConfig3,
14089
+ declareContentAddressedUrl,
14090
+ renameWithRetry as renameWithRetry6,
14091
+ declareProbeFactsUrl
14059
14092
  } from "@hyperframes/engine";
14060
14093
 
14061
14094
  // src/services/distributed/planFrameCost.ts
@@ -14125,6 +14158,7 @@ var FIELD_DELIMITER = Buffer.from([0]);
14125
14158
  import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg6 } from "@hyperframes/engine";
14126
14159
 
14127
14160
  // src/services/distributed/mp4Stitch.ts
14161
+ import { renameWithRetry as renameWithRetry5 } from "@hyperframes/engine";
14128
14162
  var SCAN_BYTES = 64 * 1024;
14129
14163
 
14130
14164
  // src/services/distributed/chunkAudio.ts