@hyperframes/producer 0.8.226 → 0.8.228

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/server.ts
4
4
  import {
5
- existsSync as existsSync20,
5
+ existsSync as existsSync21,
6
6
  mkdirSync as mkdirSync11,
7
7
  statSync as statSync10,
8
8
  mkdtempSync as mkdtempSync4,
@@ -10,7 +10,7 @@ import {
10
10
  rmSync as rmSync11,
11
11
  createReadStream as createReadStream3
12
12
  } from "node:fs";
13
- import { resolve as resolve10, dirname as dirname13, join as join31 } from "node:path";
13
+ import { resolve as resolve10, dirname as dirname14, join as join32 } from "node:path";
14
14
  import { tmpdir as tmpdir3 } from "node:os";
15
15
  import { parseArgs } from "node:util";
16
16
  import crypto from "node:crypto";
@@ -22,7 +22,7 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
22
22
  // src/services/renderOrchestrator.ts
23
23
  import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
24
24
  import {
25
- existsSync as existsSync17,
25
+ existsSync as existsSync18,
26
26
  mkdirSync as mkdirSync10,
27
27
  readdirSync as readdirSync5,
28
28
  rmSync as rmSync10,
@@ -77,7 +77,7 @@ import {
77
77
  augmentProtocolTimeoutError,
78
78
  augmentPageNavigationTimeoutError
79
79
  } from "@hyperframes/engine";
80
- import { join as join27, dirname as dirname11, resolve as resolve8 } from "path";
80
+ import { join as join28, dirname as dirname12, resolve as resolve8 } from "path";
81
81
  import { totalmem as totalmem2 } from "node:os";
82
82
  import { randomUUID } from "crypto";
83
83
  import { fileURLToPath as fileURLToPath4 } from "url";
@@ -9809,7 +9809,7 @@ async function planVideoLayerPassthrough(input) {
9809
9809
  }
9810
9810
 
9811
9811
  // src/services/render/stages/audioStage.ts
9812
- import { join as join20 } from "node:path";
9812
+ import { join as join21 } from "node:path";
9813
9813
  import {
9814
9814
  MIXED_AUDIO_FILENAME,
9815
9815
  audioFailureDiagnostics,
@@ -9818,6 +9818,16 @@ import {
9818
9818
  collectScratchLeftovers
9819
9819
  } from "@hyperframes/engine";
9820
9820
 
9821
+ // src/services/render/audioDeliveryLimit.ts
9822
+ import { existsSync as existsSync13 } from "node:fs";
9823
+ import { dirname as dirname9, join as join20 } from "node:path";
9824
+ import {
9825
+ applyAudioFxChain,
9826
+ describeFfmpegFailure as describeFfmpegFailure3,
9827
+ encodeAac as encodeAac2,
9828
+ renameWithRetry as renameWithRetry6
9829
+ } from "@hyperframes/engine";
9830
+
9821
9831
  // src/services/render/audioPadTrim.ts
9822
9832
  import { spawn } from "node:child_process";
9823
9833
  import { mkdtempSync as mkdtempSync3 } from "node:fs";
@@ -10278,6 +10288,48 @@ async function runFfprobeJson(args, signal) {
10278
10288
  }
10279
10289
  }
10280
10290
 
10291
+ // src/services/render/audioDeliveryLimit.ts
10292
+ var LIMIT_CEILING_DBTP = AAC_DELIVERY_TRUE_PEAK_DBFS - 2;
10293
+ var LIMIT_CHAIN = {
10294
+ version: 1,
10295
+ nodes: [{ type: "truepeak", id: "delivery", params: { ceiling: LIMIT_CEILING_DBTP } }]
10296
+ };
10297
+ async function limitDeliveryPeaks(input, deps = {
10298
+ measure: measureTruePeakDbfs,
10299
+ applyChain: applyAudioFxChain,
10300
+ encode: encodeAac2,
10301
+ rename: renameWithRetry6,
10302
+ sourceExists: existsSync13
10303
+ }) {
10304
+ const { audioPath, sourcePath, signal, timeoutMs } = input;
10305
+ if (!deps.sourceExists(sourcePath)) return { success: true };
10306
+ const peak = await deps.measure(audioPath, { timeoutMs, signal });
10307
+ if (peak <= AAC_DELIVERY_TRUE_PEAK_DBFS) return { success: true };
10308
+ const limitedPath = join20(dirname9(sourcePath), "audio-source-limited.wav");
10309
+ try {
10310
+ await deps.applyChain(sourcePath, LIMIT_CHAIN, limitedPath, { trackId: "delivery", signal });
10311
+ } catch (error) {
10312
+ if (signal?.aborted) throw error;
10313
+ input.warn?.("[audio] true-peak limit failed, the whole-mix cut applies", {
10314
+ error: error instanceof Error ? error.message : String(error)
10315
+ });
10316
+ return { success: true };
10317
+ }
10318
+ const encoded = await deps.encode(limitedPath, audioPath, input.targetDurationSeconds, {
10319
+ signal,
10320
+ timeout: timeoutMs
10321
+ });
10322
+ if (!encoded.success) {
10323
+ return {
10324
+ success: false,
10325
+ error: `[audioDeliveryLimit] ${describeFfmpegFailure3(encoded, timeoutMs)}`,
10326
+ failureReason: encoded.failureReason
10327
+ };
10328
+ }
10329
+ deps.rename(limitedPath, sourcePath, { copyFallback: true });
10330
+ return { success: true };
10331
+ }
10332
+
10281
10333
  // src/services/render/stages/audioStage.ts
10282
10334
  function assertAudioNotInterrupted(failures) {
10283
10335
  const interrupted = failures?.find((failure) => failure.reason === "external_interruption");
@@ -10301,13 +10353,22 @@ function runAudioStage(input) {
10301
10353
  }
10302
10354
  async function runAudioStageScoped(input) {
10303
10355
  const stageStart = Date.now();
10304
- const pcmSourcePath = join20(input.workDir, "audio-source.wav");
10356
+ const pcmSourcePath = join21(input.workDir, "audio-source.wav");
10305
10357
  const duration = input.totalFrames * input.fps.den / input.fps.num;
10306
10358
  try {
10307
10359
  const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
10308
10360
  let audioLoweredDb;
10309
10361
  if (result.hasAudio) {
10310
10362
  const guardStart = Date.now();
10363
+ const limited = await limitDeliveryPeaks({
10364
+ audioPath: result.audioOutputPath,
10365
+ sourcePath: pcmSourcePath,
10366
+ targetDurationSeconds: duration,
10367
+ signal: input.abortSignal,
10368
+ timeoutMs: input.ffmpegProcessTimeout,
10369
+ warn: (message, details) => input.log?.warn?.(message, details)
10370
+ });
10371
+ if (!limited.success) throw encoderFailureError("Audio true-peak limit failed", limited);
10311
10372
  const peak = await constrainAacTruePeak({
10312
10373
  audioPath: result.audioOutputPath,
10313
10374
  sourcePath: pcmSourcePath,
@@ -10339,7 +10400,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10339
10400
  assertNotAborted,
10340
10401
  log
10341
10402
  } = input;
10342
- const audioOutputPath = join20(workDir, MIXED_AUDIO_FILENAME);
10403
+ const audioOutputPath = join21(workDir, MIXED_AUDIO_FILENAME);
10343
10404
  let hasAudio = false;
10344
10405
  let audioError;
10345
10406
  let audioFailures;
@@ -10350,7 +10411,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10350
10411
  audioResult = await processCompositionAudio(
10351
10412
  audios,
10352
10413
  projectDir,
10353
- join20(workDir, "audio-work"),
10414
+ join21(workDir, "audio-work"),
10354
10415
  audioOutputPath,
10355
10416
  duration,
10356
10417
  abortSignal,
@@ -10418,8 +10479,8 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
10418
10479
  }
10419
10480
 
10420
10481
  // src/services/render/stages/captureStage.ts
10421
- import { existsSync as existsSync13, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10422
- import { join as join21 } from "node:path";
10482
+ import { existsSync as existsSync14, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
10483
+ import { join as join22 } from "node:path";
10423
10484
  import {
10424
10485
  classifyCaptureFailure as classifyCaptureFailure2,
10425
10486
  frameFileExtension,
@@ -10489,16 +10550,16 @@ function diskCaptureShortfallError(needBytes, freeBytes, framesDir, basis, remed
10489
10550
  }
10490
10551
  function measureCaptureFrameBytes(workDir, framesDir) {
10491
10552
  let total = sampleDirectoryBytes(framesDir);
10492
- if (!existsSync13(workDir)) return total;
10553
+ if (!existsSync14(workDir)) return total;
10493
10554
  for (const attempt of readdirSync3(workDir)) {
10494
10555
  if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
10495
- total += sampleDirectoryBytes(join21(workDir, attempt));
10556
+ total += sampleDirectoryBytes(join22(workDir, attempt));
10496
10557
  continue;
10497
10558
  }
10498
10559
  if (!/^capture-attempt-\d+$/.test(attempt)) continue;
10499
- const attemptDir = join21(workDir, attempt);
10560
+ const attemptDir = join22(workDir, attempt);
10500
10561
  for (const name of readdirSync3(attemptDir)) {
10501
- if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join21(attemptDir, name));
10562
+ if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join22(attemptDir, name));
10502
10563
  }
10503
10564
  }
10504
10565
  return total;
@@ -10792,8 +10853,8 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
10792
10853
  }
10793
10854
 
10794
10855
  // src/services/render/stages/captureHdrStage.ts
10795
- import { existsSync as existsSync15, mkdirSync as mkdirSync8 } from "node:fs";
10796
- import { join as join25 } from "node:path";
10856
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8 } from "node:fs";
10857
+ import { join as join26 } from "node:path";
10797
10858
  import {
10798
10859
  closeCaptureSession as closeCaptureSession7,
10799
10860
  cloneCaptureWarnings as cloneCaptureWarnings2,
@@ -10865,7 +10926,7 @@ function createHdrImageTransferCache(options = {}) {
10865
10926
  }
10866
10927
 
10867
10928
  // src/services/render/stages/captureHdrSequentialLoop.ts
10868
- import { join as join22 } from "node:path";
10929
+ import { join as join23 } from "node:path";
10869
10930
  import {
10870
10931
  TRANSITIONS,
10871
10932
  crossfade
@@ -10990,7 +11051,7 @@ async function runSequentialLayeredFrameLoop(input) {
10990
11051
  );
10991
11052
  if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
10992
11053
  writeFileExclusiveSync(
10993
- join22(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
11054
+ join23(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
10994
11055
  normalCanvas
10995
11056
  );
10996
11057
  }
@@ -11022,7 +11083,7 @@ async function runSequentialLayeredFrameLoop(input) {
11022
11083
  }
11023
11084
 
11024
11085
  // src/services/render/stages/captureHdrHybridLoop.ts
11025
- import { join as join24 } from "node:path";
11086
+ import { join as join25 } from "node:path";
11026
11087
  import {
11027
11088
  TRANSITIONS as TRANSITIONS2,
11028
11089
  closeCaptureSession as closeCaptureSession6,
@@ -11036,9 +11097,9 @@ import {
11036
11097
  // src/services/shaderTransitionWorkerPool.ts
11037
11098
  import { Worker } from "node:worker_threads";
11038
11099
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
11039
- import { dirname as dirname9, join as join23 } from "node:path";
11100
+ import { dirname as dirname10, join as join24 } from "node:path";
11040
11101
  import { createRequire } from "node:module";
11041
- import { existsSync as existsSync14 } from "node:fs";
11102
+ import { existsSync as existsSync15 } from "node:fs";
11042
11103
  import { cpus as cpus2 } from "node:os";
11043
11104
  function resolveWorkerEntry(explicit) {
11044
11105
  if (explicit && explicit.length > 0) {
@@ -11049,10 +11110,10 @@ function resolveWorkerEntry(explicit) {
11049
11110
  const isTs = override.endsWith(".ts");
11050
11111
  return { path: override, isTs };
11051
11112
  }
11052
- const moduleDir = dirname9(fileURLToPath3(import.meta.url));
11053
- const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
11054
- if (existsSync14(jsPath)) return { path: jsPath, isTs: false };
11055
- const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
11113
+ const moduleDir = dirname10(fileURLToPath3(import.meta.url));
11114
+ const jsPath = join24(moduleDir, "shaderTransitionWorker.js");
11115
+ if (existsSync15(jsPath)) return { path: jsPath, isTs: false };
11116
+ const tsPath = join24(moduleDir, "shaderTransitionWorker.ts");
11056
11117
  return { path: tsPath, isTs: true };
11057
11118
  }
11058
11119
  function buildExecArgv(entryIsTs) {
@@ -11420,7 +11481,7 @@ async function runHybridLayeredFrameLoop(input) {
11420
11481
  );
11421
11482
  if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
11422
11483
  writeFileExclusiveSync(
11423
- join24(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
11484
+ join25(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
11424
11485
  canvas
11425
11486
  );
11426
11487
  }
@@ -11611,8 +11672,8 @@ async function runCaptureHdrStage(input) {
11611
11672
  if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
11612
11673
  }
11613
11674
  const debugDumpEnabled = process.env.KEEP_TEMP === "1";
11614
- const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
11615
- if (debugDumpDir && !existsSync15(debugDumpDir)) {
11675
+ const debugDumpDir = debugDumpEnabled ? join26(framesDir, "debug-composite") : null;
11676
+ if (debugDumpDir && !existsSync16(debugDumpDir)) {
11616
11677
  mkdirSync8(debugDumpDir, { recursive: true });
11617
11678
  }
11618
11679
  const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
@@ -11769,7 +11830,7 @@ async function runCaptureHdrStage(input) {
11769
11830
  // src/services/render/stages/encodeStage.ts
11770
11831
  import {
11771
11832
  copyFileSync as copyFileSync4,
11772
- existsSync as existsSync16,
11833
+ existsSync as existsSync17,
11773
11834
  mkdirSync as mkdirSync9,
11774
11835
  readdirSync as readdirSync4,
11775
11836
  readFileSync as readFileSync7,
@@ -11778,7 +11839,7 @@ import {
11778
11839
  writeFileSync as writeFileSync5
11779
11840
  } from "node:fs";
11780
11841
  import { readFile as readFile2 } from "node:fs/promises";
11781
- import { dirname as dirname10, join as join26 } from "node:path";
11842
+ import { dirname as dirname11, join as join27 } from "node:path";
11782
11843
  import {
11783
11844
  encodeFramesChunkedConcat,
11784
11845
  encodeFramesFromDir,
@@ -11903,7 +11964,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
11903
11964
  const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
11904
11965
  if (failure) return failure;
11905
11966
  }
11906
- const fileSize = existsSync16(outputPath) ? statSync7(outputPath).size : 0;
11967
+ const fileSize = existsSync17(outputPath) ? statSync7(outputPath).size : 0;
11907
11968
  return {
11908
11969
  success: true,
11909
11970
  outputPath,
@@ -11989,7 +12050,7 @@ function encodeWhileCapturing(input, totalFrames) {
11989
12050
  await new Promise((resolve11) => wake = resolve11);
11990
12051
  }
11991
12052
  if (signal.aborted) return;
11992
- const mergedPath = join26(input.framesDir, formatCaptureFrameName(next, "jpg"));
12053
+ const mergedPath = join27(input.framesDir, formatCaptureFrameName(next, "jpg"));
11993
12054
  const frame = await readLandedFrame(landed.get(next), mergedPath);
11994
12055
  landed.delete(next);
11995
12056
  if (!await stream.writeFrame(frame)) {
@@ -12064,7 +12125,7 @@ async function runEncodeStage(input) {
12064
12125
  const stage5Start = Date.now();
12065
12126
  if (isPngSequence) {
12066
12127
  const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
12067
- if (!existsSync16(outputPath)) mkdirSync9(outputPath, { recursive: true });
12128
+ if (!existsSync17(outputPath)) mkdirSync9(outputPath, { recursive: true });
12068
12129
  const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
12069
12130
  if (captured.length === 0) {
12070
12131
  throw new Error(
@@ -12072,11 +12133,11 @@ async function runEncodeStage(input) {
12072
12133
  );
12073
12134
  }
12074
12135
  captured.forEach((name, i) => {
12075
- const dst = join26(outputPath, formatExportFrameName(i, "png"));
12076
- copyFileSync4(join26(framesDir, name), dst);
12136
+ const dst = join27(outputPath, formatExportFrameName(i, "png"));
12137
+ copyFileSync4(join27(framesDir, name), dst);
12077
12138
  });
12078
- if (hasAudio && audioOutputPath && existsSync16(audioOutputPath)) {
12079
- copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
12139
+ if (hasAudio && audioOutputPath && existsSync17(audioOutputPath)) {
12140
+ copyFileSync4(audioOutputPath, join27(outputPath, MIXED_AUDIO_FILENAME2));
12080
12141
  log.info(
12081
12142
  `[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
12082
12143
  );
@@ -12096,7 +12157,7 @@ async function runEncodeStage(input) {
12096
12157
  const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
12097
12158
  fps: job.config.fps,
12098
12159
  loop,
12099
- palettePath: join26(dirname10(videoOnlyPath), "gif-palette.png"),
12160
+ palettePath: join27(dirname11(videoOnlyPath), "gif-palette.png"),
12100
12161
  preserveAlpha: needsAlpha,
12101
12162
  signal: abortSignal,
12102
12163
  timeout: engineCfg.ffmpegEncodeTimeout
@@ -12306,7 +12367,7 @@ function sampleDirectoryBytes(dir) {
12306
12367
  continue;
12307
12368
  }
12308
12369
  for (const name of entries) {
12309
- const full = join27(current, name);
12370
+ const full = join28(current, name);
12310
12371
  try {
12311
12372
  const st = statSync8(full);
12312
12373
  if (st.isDirectory()) {
@@ -12455,8 +12516,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
12455
12516
  const ranges = [];
12456
12517
  let rangeStart = null;
12457
12518
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12458
- const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12459
- const missing = !existsSync17(framePath) || statSync8(framePath).size <= 8;
12519
+ const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12520
+ const missing = !existsSync18(framePath) || statSync8(framePath).size <= 8;
12460
12521
  if (missing && rangeStart === null) {
12461
12522
  rangeStart = frameIndex;
12462
12523
  } else if (!missing && rangeStart !== null) {
@@ -12472,7 +12533,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
12472
12533
  function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
12473
12534
  const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
12474
12535
  const batches = [];
12475
- const outputDirFor = (batchIndex, workerId) => join27(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
12536
+ const outputDirFor = (batchIndex, workerId) => join28(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
12476
12537
  if (stride > 1) {
12477
12538
  const spans = /* @__PURE__ */ new Map();
12478
12539
  for (const range of ranges) {
@@ -12532,8 +12593,8 @@ function getNextRetryWorkerCount(currentWorkers) {
12532
12593
  return Math.max(1, Math.floor(currentWorkers / 2));
12533
12594
  }
12534
12595
  function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
12535
- if (platform2 === "win32" || platform2 === "darwin") return join27(systemTempDir, "hf-render-");
12536
- return join27(dirname11(outputPath), `work-${jobId}-`);
12596
+ if (platform2 === "win32" || platform2 === "darwin") return join28(systemTempDir, "hf-render-");
12597
+ return join28(dirname12(outputPath), `work-${jobId}-`);
12537
12598
  }
12538
12599
  var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
12539
12600
  function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
@@ -12580,8 +12641,8 @@ The composition is too large for the available memory. To reduce memory pressure
12580
12641
  function countCapturedFrames(totalFrames, framesDir, frameExt) {
12581
12642
  let captured = 0;
12582
12643
  for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
12583
- const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12584
- if (existsSync17(framePath)) captured++;
12644
+ const framePath = join28(framesDir, formatCaptureFrameName(frameIndex, frameExt));
12645
+ if (existsSync18(framePath)) captured++;
12585
12646
  }
12586
12647
  return captured;
12587
12648
  }
@@ -12606,7 +12667,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
12606
12667
  reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
12607
12668
  });
12608
12669
  pendingTransientRetry = false;
12609
- const attemptWorkDir = join27(options.workDir, `capture-attempt-${attempt}`);
12670
+ const attemptWorkDir = join28(options.workDir, `capture-attempt-${attempt}`);
12610
12671
  const batches = missingRanges ? buildMissingFrameRetryBatches(
12611
12672
  missingRanges,
12612
12673
  currentWorkers,
@@ -13236,19 +13297,19 @@ function deVerifyFallbackTelemetry(err) {
13236
13297
  };
13237
13298
  }
13238
13299
  function resolveRenderDebugDir() {
13239
- const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname11(fileURLToPath4(import.meta.url)), "../..");
13240
- return join27(producerRoot, ".debug");
13300
+ const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname12(fileURLToPath4(import.meta.url)), "../..");
13301
+ return join28(producerRoot, ".debug");
13241
13302
  }
13242
13303
  async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
13243
13304
  validateHlsRenderConfig(job.config);
13244
13305
  validateDeliveryEncoderConfig(job.config);
13245
13306
  const debugDir = resolveRenderDebugDir();
13246
- const outputDir = dirname11(outputPath);
13247
- if (!existsSync17(outputDir)) mkdirSync10(outputDir, { recursive: true });
13248
- const workDir = job.config.debug ? join27(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
13307
+ const outputDir = dirname12(outputPath);
13308
+ if (!existsSync18(outputDir)) mkdirSync10(outputDir, { recursive: true });
13309
+ const workDir = job.config.debug ? join28(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
13249
13310
  const pipelineStart = Date.now();
13250
13311
  const baseLog = job.config.logger ?? defaultLogger;
13251
- const logPath = job.config.debug ? join27(workDir, "render.log") : null;
13312
+ const logPath = job.config.debug ? join28(workDir, "render.log") : null;
13252
13313
  const execution = new RenderExecutionContext({
13253
13314
  request: { renderJobId: job.id, projectDir, outputPath },
13254
13315
  logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
@@ -13309,7 +13370,7 @@ async function executeRenderPipeline(input) {
13309
13370
  imageDecodeFailures: 0
13310
13371
  };
13311
13372
  let hdrPerf;
13312
- const perfOutputPath = join27(workDir, "perf-summary.json");
13373
+ const perfOutputPath = join28(workDir, "perf-summary.json");
13313
13374
  const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
13314
13375
  const observability = new RenderObservabilityRecorder({
13315
13376
  pipelineStartMs: pipelineStart,
@@ -13385,7 +13446,7 @@ async function executeRenderPipeline(input) {
13385
13446
  job.startedAt = /* @__PURE__ */ new Date();
13386
13447
  assertNotAborted();
13387
13448
  assertConfiguredFfmpegBinariesExist();
13388
- if (!existsSync17(workDir)) mkdirSync10(workDir, { recursive: true });
13449
+ if (!existsSync18(workDir)) mkdirSync10(workDir, { recursive: true });
13389
13450
  if (job.config.debug) {
13390
13451
  log.info("[Render] Debug artifacts enabled", { workDir, logPath });
13391
13452
  }
@@ -13414,16 +13475,16 @@ async function executeRenderPipeline(input) {
13414
13475
  requestedWorkers: job.config.workers ?? "auto"
13415
13476
  });
13416
13477
  const entryFile = job.config.entryFile || "index.html";
13417
- let htmlPath = join27(projectDir, entryFile);
13418
- if (!existsSync17(htmlPath)) {
13478
+ let htmlPath = join28(projectDir, entryFile);
13479
+ if (!existsSync18(htmlPath)) {
13419
13480
  throw new Error(`Entry file not found: ${htmlPath}`);
13420
13481
  }
13421
13482
  assertNotAborted();
13422
13483
  const rawEntry = readTextFile2(htmlPath);
13423
13484
  if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
13424
- const wrapperPath = join27(workDir, "standalone-entry.html");
13425
- const projectIndexPath = join27(projectDir, "index.html");
13426
- if (!existsSync17(projectIndexPath)) {
13485
+ const wrapperPath = join28(workDir, "standalone-entry.html");
13486
+ const projectIndexPath = join28(projectDir, "index.html");
13487
+ if (!existsSync18(projectIndexPath)) {
13427
13488
  throw new Error(
13428
13489
  `Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
13429
13490
  );
@@ -13613,7 +13674,7 @@ async function executeRenderPipeline(input) {
13613
13674
  canvas: { width, height },
13614
13675
  page: probeSession.page,
13615
13676
  projectDir,
13616
- compiledDir: join27(workDir, "compiled")
13677
+ compiledDir: join28(workDir, "compiled")
13617
13678
  });
13618
13679
  if (videoLayerPlan.videoId === void 0) {
13619
13680
  log.info(`[Render] Video layer passthrough off: ${videoLayerPlan.reason}`);
@@ -13631,7 +13692,7 @@ async function executeRenderPipeline(input) {
13631
13692
  updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
13632
13693
  code: "extract_video"
13633
13694
  });
13634
- const compiledDir = join27(workDir, "compiled");
13695
+ const compiledDir = join28(workDir, "compiled");
13635
13696
  const extractResult = await observeRenderStage(
13636
13697
  observability,
13637
13698
  "video_extract",
@@ -13743,7 +13804,7 @@ async function executeRenderPipeline(input) {
13743
13804
  try {
13744
13805
  fileServer = await createFileServer({
13745
13806
  projectDir,
13746
- compiledDir: join27(workDir, "compiled"),
13807
+ compiledDir: join28(workDir, "compiled"),
13747
13808
  port: 0,
13748
13809
  preHeadScripts: [VIRTUAL_TIME_SHIM],
13749
13810
  fps: job.config.fps
@@ -13761,8 +13822,8 @@ async function executeRenderPipeline(input) {
13761
13822
  if (!activeFileServer) {
13762
13823
  throw new Error("File server failed to initialize before frame capture");
13763
13824
  }
13764
- const framesDir = join27(workDir, "captured-frames");
13765
- if (!existsSync17(framesDir)) mkdirSync10(framesDir, { recursive: true });
13825
+ const framesDir = join28(workDir, "captured-frames");
13826
+ if (!existsSync18(framesDir)) mkdirSync10(framesDir, { recursive: true });
13766
13827
  updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
13767
13828
  code: "start_browsers"
13768
13829
  });
@@ -14259,7 +14320,7 @@ async function executeRenderPipeline(input) {
14259
14320
  hls: ".mp4"
14260
14321
  };
14261
14322
  const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
14262
- const videoOnlyPath = join27(workDir, `video-only${videoExt}`);
14323
+ const videoOnlyPath = join28(workDir, `video-only${videoExt}`);
14263
14324
  const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
14264
14325
  if (usePageSideCompositingForTransitions) {
14265
14326
  activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
@@ -14547,7 +14608,7 @@ async function executeRenderPipeline(input) {
14547
14608
  pageMotionBlur: job.config.pageMotionBlur,
14548
14609
  captureQuality: captureOptions.quality
14549
14610
  });
14550
- const segmentDir = segmentDirFor(join27(projectDir, "renders"), segmentPlanHash);
14611
+ const segmentDir = segmentDirFor(join28(projectDir, "renders"), segmentPlanHash);
14551
14612
  let completedSegments = /* @__PURE__ */ new Set();
14552
14613
  if (job.config.resumeSegments === true) {
14553
14614
  const existing = readSegmentManifest(segmentDir);
@@ -15084,7 +15145,7 @@ async function executeRenderPipeline(input) {
15084
15145
  })
15085
15146
  );
15086
15147
  const totalElapsed = Date.now() - pipelineStart;
15087
- const tmpPeakBytes = existsSync17(workDir) ? sampleDirectoryBytes(workDir) : 0;
15148
+ const tmpPeakBytes = existsSync18(workDir) ? sampleDirectoryBytes(workDir) : 0;
15088
15149
  recordTransientRetryObservability();
15089
15150
  observability.checkpoint("pipeline", "artifact validated", { totalElapsedMs: totalElapsed });
15090
15151
  const observabilitySummary = observability.summary({
@@ -15165,8 +15226,8 @@ async function executeRenderPipeline(input) {
15165
15226
  }
15166
15227
  }
15167
15228
  if (job.config.debug) {
15168
- if (!isPngSequence && !isHls && existsSync17(stagedOutputPath)) {
15169
- const debugOutput = join27(workDir, `output${videoExt}`);
15229
+ if (!isPngSequence && !isHls && existsSync18(stagedOutputPath)) {
15230
+ const debugOutput = join28(workDir, `output${videoExt}`);
15170
15231
  copyFileSync5(stagedOutputPath, debugOutput);
15171
15232
  }
15172
15233
  }
@@ -15272,7 +15333,7 @@ async function executeRenderPipeline(input) {
15272
15333
 
15273
15334
  // src/services/hyperframeLint.ts
15274
15335
  import {
15275
- existsSync as existsSync18,
15336
+ existsSync as existsSync19,
15276
15337
  readFileSync as readFileSync8,
15277
15338
  statSync as statSync9,
15278
15339
  openSync as openSync5,
@@ -15280,7 +15341,7 @@ import {
15280
15341
  closeSync as closeSync5,
15281
15342
  constants as constants3
15282
15343
  } from "node:fs";
15283
- import { resolve as resolve9, join as join28, relative as relative3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
15344
+ import { resolve as resolve9, join as join29, relative as relative3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
15284
15345
  import { lintHyperframeHtml } from "@hyperframes/lint";
15285
15346
  function isStringRecord(value) {
15286
15347
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -15326,7 +15387,7 @@ function readEntryHtml(filePath) {
15326
15387
  }
15327
15388
  function readProjectEntryFile(projectDir, preferredEntryFile) {
15328
15389
  const absProjectDir = resolve9(projectDir);
15329
- if (!existsSync18(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
15390
+ if (!existsSync19(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
15330
15391
  return { error: `Project directory not found: ${absProjectDir}` };
15331
15392
  }
15332
15393
  const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
@@ -15348,7 +15409,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
15348
15409
  }
15349
15410
  }
15350
15411
  return {
15351
- error: `No HTML entry file found in project directory: ${join28(absProjectDir, preferredEntryFile || "index.html")}`
15412
+ error: `No HTML entry file found in project directory: ${join29(absProjectDir, preferredEntryFile || "index.html")}`
15352
15413
  };
15353
15414
  }
15354
15415
  function prepareHyperframeLintBody(body) {
@@ -15394,8 +15455,8 @@ async function runHyperframeLint(prepared) {
15394
15455
  // src/services/healthWorker.ts
15395
15456
  import { Worker as Worker2 } from "node:worker_threads";
15396
15457
  import { fileURLToPath as fileURLToPath5 } from "node:url";
15397
- import { dirname as dirname12, join as join29 } from "node:path";
15398
- import { existsSync as existsSync19 } from "node:fs";
15458
+ import { dirname as dirname13, join as join30 } from "node:path";
15459
+ import { existsSync as existsSync20 } from "node:fs";
15399
15460
  var DEFAULT_HEALTH_PORT = 9848;
15400
15461
  async function startHealthWorker(options = {}) {
15401
15462
  const log = options.logger ?? defaultLogger2();
@@ -15469,10 +15530,10 @@ function defaultLogger2() {
15469
15530
  };
15470
15531
  }
15471
15532
  function resolveWorkerEntry2() {
15472
- const here = dirname12(fileURLToPath5(import.meta.url));
15473
- const candidates = [join29(here, "healthWorkerThread.js"), join29(here, "healthWorkerThread.ts")];
15533
+ const here = dirname13(fileURLToPath5(import.meta.url));
15534
+ const candidates = [join30(here, "healthWorkerThread.js"), join30(here, "healthWorkerThread.ts")];
15474
15535
  for (const candidate of candidates) {
15475
- if (existsSync19(candidate)) return candidate;
15536
+ if (existsSync20(candidate)) return candidate;
15476
15537
  }
15477
15538
  return void 0;
15478
15539
  }
@@ -15507,7 +15568,7 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
15507
15568
 
15508
15569
  // src/services/distributed/plan.ts
15509
15570
  import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
15510
- import { extname as extname4, isAbsolute as isAbsolute4, join as join30, relative as relative4, sep as sep3 } from "node:path";
15571
+ import { extname as extname4, isAbsolute as isAbsolute4, join as join31, relative as relative4, sep as sep3 } from "node:path";
15511
15572
  import {
15512
15573
  fpsToNumber as fpsToNumber9,
15513
15574
  HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
@@ -15518,7 +15579,7 @@ import {
15518
15579
  normalizeVp9CpuUsed,
15519
15580
  resolveConfig as resolveConfig3,
15520
15581
  declareContentAddressedUrl,
15521
- renameWithRetry as renameWithRetry7,
15582
+ renameWithRetry as renameWithRetry8,
15522
15583
  declareProbeFactsUrl
15523
15584
  } from "@hyperframes/engine";
15524
15585
 
@@ -15589,10 +15650,10 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
15589
15650
  var FIELD_DELIMITER = Buffer.from([0]);
15590
15651
 
15591
15652
  // src/services/distributed/chunkAudio.ts
15592
- import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
15653
+ import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure4, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
15593
15654
 
15594
15655
  // src/services/distributed/mp4Stitch.ts
15595
- import { renameWithRetry as renameWithRetry6 } from "@hyperframes/engine";
15656
+ import { renameWithRetry as renameWithRetry7 } from "@hyperframes/engine";
15596
15657
  var SCAN_BYTES = 64 * 1024;
15597
15658
 
15598
15659
  // src/services/distributed/chunkAudio.ts
@@ -15612,9 +15673,9 @@ var PLAN_ROOT_CATEGORY = {
15612
15673
  var PLAN_DIR_SIZE_LIMIT_BYTES = 2 * 1024 * 1024 * 1024;
15613
15674
  var FREEZE_OWNED_PLAN_FILES = [
15614
15675
  "plan.json",
15615
- join30("meta", "composition.json"),
15616
- join30("meta", "encoder.json"),
15617
- join30("meta", "chunks.json")
15676
+ join31("meta", "composition.json"),
15677
+ join31("meta", "encoder.json"),
15678
+ join31("meta", "chunks.json")
15618
15679
  ];
15619
15680
 
15620
15681
  // src/services/distributed/renderConfigValidation.ts
@@ -15998,8 +16059,8 @@ function buildRenderJobConfig(input, outputPath, log) {
15998
16059
  function resolvePreparedRenderOutput(prepared, rendersDir, log) {
15999
16060
  const { input, cleanupProjectDir } = prepared;
16000
16061
  const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
16001
- const outputDir = dirname13(absoluteOutputPath);
16002
- if (!existsSync20(outputDir)) mkdirSync11(outputDir, { recursive: true });
16062
+ const outputDir = dirname14(absoluteOutputPath);
16063
+ if (!existsSync21(outputDir)) mkdirSync11(outputDir, { recursive: true });
16003
16064
  return { input, cleanupProjectDir, absoluteOutputPath };
16004
16065
  }
16005
16066
  function validateRenderOverrides(body) {
@@ -16040,11 +16101,11 @@ function prepareProjectDirectory(projectDir, options) {
16040
16101
  const candidate = nonEmptyString(projectDir);
16041
16102
  if (!candidate) return null;
16042
16103
  const absProjectDir = resolve10(candidate);
16043
- if (!existsSync20(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
16104
+ if (!existsSync21(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
16044
16105
  return { error: `Project directory not found: ${absProjectDir}` };
16045
16106
  }
16046
16107
  const entry = options.entryFile || "index.html";
16047
- if (!existsSync20(resolve10(absProjectDir, entry))) {
16108
+ if (!existsSync21(resolve10(absProjectDir, entry))) {
16048
16109
  return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
16049
16110
  }
16050
16111
  return { prepared: { input: { projectDir: absProjectDir, ...options } } };
@@ -16065,8 +16126,8 @@ async function resolveInlineRenderHtml(body) {
16065
16126
  }
16066
16127
  function materializeInlineProject(html, options) {
16067
16128
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
16068
- const tempProjectDir = mkdtempSync4(join31(tempRoot, "producer-project-"));
16069
- writeFileSync7(join31(tempProjectDir, "index.html"), html, "utf-8");
16129
+ const tempProjectDir = mkdtempSync4(join32(tempRoot, "producer-project-"));
16130
+ writeFileSync7(join32(tempProjectDir, "index.html"), html, "utf-8");
16070
16131
  return {
16071
16132
  prepared: {
16072
16133
  input: { projectDir: tempProjectDir, ...options },
@@ -16136,7 +16197,7 @@ function cleanupTempDir(dir, log) {
16136
16197
  }
16137
16198
  }
16138
16199
  function outputFileSize(path) {
16139
- return existsSync20(path) ? statSync10(path).size : 0;
16200
+ return existsSync21(path) ? statSync10(path).size : 0;
16140
16201
  }
16141
16202
  function createBlockingProgressReporter(log, requestId) {
16142
16203
  let lastLoggedPct = -10;
@@ -16438,7 +16499,7 @@ function createRenderHandlers(options = {}) {
16438
16499
  if (!artifact) {
16439
16500
  return c.json({ success: false, error: "Output artifact not found or expired" }, 404);
16440
16501
  }
16441
- if (!existsSync20(artifact.path)) {
16502
+ if (!existsSync21(artifact.path)) {
16442
16503
  store.delete(token);
16443
16504
  return c.json({ success: false, error: "Output artifact file missing" }, 404);
16444
16505
  }