@hyperframes/producer 0.8.199 → 0.8.200

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.
@@ -2311,10 +2311,6 @@ var THREE_D_CONTEXT_PATTERN = /transform-style\s*:\s*preserve-3d|backface-visibi
2311
2311
  function detectThreeDTransformUsage(html) {
2312
2312
  return THREE_D_CONTEXT_PATTERN.test(html);
2313
2313
  }
2314
- var MIX_BLEND_MODE_PATTERN = /mix-blend-mode\s*:/i;
2315
- function detectMixBlendModeUsage(html) {
2316
- return MIX_BLEND_MODE_PATTERN.test(html);
2317
- }
2318
2314
  var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
2319
2315
  function detectAncestorBackgroundImage(html) {
2320
2316
  const { document: document2 } = parseHTML4(html);
@@ -3327,7 +3323,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
3327
3323
  const renderModeHints = detectRenderModeHints(sanitizedHtml, options.pageMotionBlur);
3328
3324
  const hasShaderTransitions = detectShaderTransitionUsage(sanitizedHtml);
3329
3325
  const usesThreeDTransforms = detectThreeDTransformUsage(sanitizedHtml);
3330
- const usesMixBlendMode = detectMixBlendModeUsage(sanitizedHtml);
3331
3326
  const hasAncestorBackgroundImage = detectAncestorBackgroundImage(sanitizedHtml);
3332
3327
  const normalizedFontHtml = normalizeSystemFontPrimaryFamilies(
3333
3328
  injectTextRenderingRule(
@@ -3441,7 +3436,6 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
3441
3436
  renderModeHints,
3442
3437
  hasShaderTransitions,
3443
3438
  usesThreeDTransforms,
3444
- usesMixBlendMode,
3445
3439
  hasAncestorBackgroundImage
3446
3440
  };
3447
3441
  }
@@ -3848,13 +3842,6 @@ async function runCompileStage(input) {
3848
3842
  "[Render] Fast capture: composition uses a CSS 3D rendering context (perspective / preserve-3d / backface-visibility), so disabling drawElementImage for this render. Capture uses the platform's baseline route."
3849
3843
  );
3850
3844
  }
3851
- if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_BLEND !== "true" && compiled.usesMixBlendMode) {
3852
- cfg.useDrawElement = false;
3853
- deCompileGate = "mix_blend_mode";
3854
- log.info(
3855
- "[Render] Fast capture: composition uses mix-blend-mode, so disabling drawElementImage for this render. Capture uses the platform's baseline route."
3856
- );
3857
- }
3858
3845
  if (cfg.useDrawElement && process.env.HF_FAST_CAPTURE_ANCESTOR_BG !== "true" && compiled.hasAncestorBackgroundImage) {
3859
3846
  cfg.useDrawElement = false;
3860
3847
  deCompileGate = "ancestor_background_image";
@@ -5312,10 +5299,11 @@ async function constrainAacTruePeak(input) {
5312
5299
  const timeoutMs = input.timeoutMs ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
5313
5300
  return enforceAacTruePeak({
5314
5301
  audioPath: input.audioPath,
5302
+ sourcePath: input.sourcePath,
5315
5303
  targetDurationSeconds: input.targetDurationSeconds,
5316
5304
  signal: input.signal,
5317
- probeTruePeak: (audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal }),
5318
- encode: defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
5305
+ probeTruePeak: input.probeTruePeakDbfs ?? ((audioPath, signal) => measureTruePeakDbfs(audioPath, { timeoutMs, signal })),
5306
+ encode: input.encodeAac ?? defaultAacEncoder(timeoutMs, input.signal, input.sourcePath)
5319
5307
  });
5320
5308
  }
5321
5309
  async function enforceAacTruePeak(input) {
@@ -5325,6 +5313,7 @@ async function enforceAacTruePeak(input) {
5325
5313
  const correctedPath = join9(scratchDir, "audio.m4a");
5326
5314
  const measurements = [];
5327
5315
  let attenuationDb = 0;
5316
+ let sourcePeakDbfs;
5328
5317
  let measuredPath = input.audioPath;
5329
5318
  for (let pass = 0; pass <= MAX_TRUE_PEAK_CORRECTION_PASSES; pass += 1) {
5330
5319
  const truePeakDbfs = await input.probeTruePeak(measuredPath, input.signal);
@@ -5341,7 +5330,11 @@ async function enforceAacTruePeak(input) {
5341
5330
  }
5342
5331
  if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
5343
5332
  const headroomDb = AAC_TRUE_PEAK_CORRECTION_HEADROOM_DB * 2 ** pass;
5344
- attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - truePeakDbfs;
5333
+ if (input.sourcePath && sourcePeakDbfs === void 0) {
5334
+ sourcePeakDbfs = await input.probeTruePeak(input.sourcePath, input.signal);
5335
+ }
5336
+ const referenceDbfs = Math.max(truePeakDbfs, (sourcePeakDbfs ?? -Infinity) + attenuationDb);
5337
+ attenuationDb += AAC_DELIVERY_TRUE_PEAK_DBFS - headroomDb - referenceDbfs;
5345
5338
  const result = await input.encode(
5346
5339
  input.audioPath,
5347
5340
  correctedPath,
@@ -5758,7 +5751,7 @@ async function runCaptureStage(input) {
5758
5751
  );
5759
5752
  }
5760
5753
  }
5761
- const captureOptions = buildCaptureOptions();
5754
+ const captureOptions = { ...buildCaptureOptions(), deFrameFallback: false };
5762
5755
  const remedy = diskCaptureRemedy(cfg, job.config.format ?? "mp4", job.duration ?? 0, workerCount);
5763
5756
  const checkDiskProjection = createDiskCaptureProjection({
5764
5757
  framesDir,
@@ -7336,6 +7329,18 @@ function planFrameWeights(input) {
7336
7329
  }
7337
7330
  return weights;
7338
7331
  }
7332
+ function withChunkCaptureCost(slices, weights) {
7333
+ const lightest = weights.reduce((least, weight) => Math.min(least, weight), Infinity);
7334
+ return slices.map((slice) => {
7335
+ let sum2 = 0;
7336
+ for (let frame = slice.startFrame; frame < slice.endFrame; frame++) sum2 += weights[frame];
7337
+ const cost = Math.round(sum2 / (slice.endFrame - slice.startFrame) / lightest * 100) / 100;
7338
+ return cost > 1 ? { ...slice, captureCost: cost } : slice;
7339
+ });
7340
+ }
7341
+ function effectiveChunkFrames(frames, captureCost = 1) {
7342
+ return Math.round(frames * captureCost);
7343
+ }
7339
7344
  function buildWeightedChunkSlices(weights, chunkCount, minFrames, maxFrames) {
7340
7345
  if (isUniformFrameCost(weights)) return null;
7341
7346
  const totalFrames = weights.length;
@@ -8660,7 +8665,10 @@ function buildPlanChunkSlices(frameWeights, config, chunkCount, effectiveChunkSi
8660
8665
  MIN_CHUNK_SIZE,
8661
8666
  Math.min(config.targetChunkFrames ?? MAX_CHUNK_SIZE, MAX_CHUNK_SIZE, maxFramesPerChunk)
8662
8667
  ) : null;
8663
- return weighted ?? buildChunkSlices(frameWeights.length, chunkCount, effectiveChunkSize);
8668
+ return withChunkCaptureCost(
8669
+ weighted ?? buildChunkSlices(frameWeights.length, chunkCount, effectiveChunkSize),
8670
+ frameWeights
8671
+ );
8664
8672
  }
8665
8673
  async function readFontSnapshotSha() {
8666
8674
  const module = await import("@hyperframes/core/fonts/embedded-data");
@@ -9651,7 +9659,12 @@ function parseChunkSlices(value) {
9651
9659
  throw new PlanV2IntegrityError(`meta/chunks.json contains duplicate chunk index ${index}`);
9652
9660
  }
9653
9661
  indexes.add(index);
9654
- return { index, startFrame, endFrame };
9662
+ if (chunk.captureCost === void 0) return { index, startFrame, endFrame };
9663
+ const captureCost = chunk.captureCost;
9664
+ if (typeof captureCost !== "number" || !Number.isFinite(captureCost) || captureCost <= 1) {
9665
+ throw new PlanV2IntegrityError(`${field}.captureCost must be a number above 1`);
9666
+ }
9667
+ return { index, startFrame, endFrame, captureCost };
9655
9668
  });
9656
9669
  }
9657
9670
  function buildVideoChunkDependencies(executionPlanDir, dimensions) {
@@ -10828,15 +10841,17 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10828
10841
  deferredVideos.filter((video) => remoteVideoIds.has(video.videoId))
10829
10842
  )
10830
10843
  });
10844
+ const captureFrames = effectiveChunkFrames(framesInChunk, slice.captureCost);
10831
10845
  const chunkWorkerCount = resolveChunkCaptureWorkers(
10832
- framesInChunk,
10833
- calculateOptimalWorkers2(framesInChunk, void 0, cfg),
10846
+ captureFrames,
10847
+ calculateOptimalWorkers2(captureFrames, void 0, cfg),
10834
10848
  options?.maxCaptureWorkers
10835
10849
  );
10836
10850
  let session = null;
10837
10851
  let outputKind;
10838
10852
  let framesEncoded = 0;
10839
10853
  let sessionBootMs = 0;
10854
+ let videoExtractMs = 0;
10840
10855
  let captureStageMs = 0;
10841
10856
  let encodeStageMs = 0;
10842
10857
  let captureMode;
@@ -10902,6 +10917,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10902
10917
  session = null;
10903
10918
  }
10904
10919
  }
10920
+ const extractStarted = Date.now();
10905
10921
  const deferredVideoFrames = planVideos ? extractDeferredVideoFramesForChunk({
10906
10922
  planDir,
10907
10923
  planVideos,
@@ -10911,6 +10927,9 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
10911
10927
  cfg,
10912
10928
  remoteSources,
10913
10929
  shownBoxes
10930
+ }).then((frames) => {
10931
+ videoExtractMs = Date.now() - extractStarted;
10932
+ return frames;
10914
10933
  }) : Promise.resolve([]);
10915
10934
  extraction = deferredVideoFrames;
10916
10935
  const videoFrameLookup = planVideos && planVideos.extracted.length > 0 ? deferredVideoFrames.then(
@@ -11128,6 +11147,8 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
11128
11147
  sessionBootMs,
11129
11148
  captureStageMs,
11130
11149
  encodeStageMs,
11150
+ // Wall time of the chunk's own video frame extraction; it overlaps captureStageMs, so do not add them.
11151
+ videoExtractMs,
11131
11152
  workers: chunkWorkerCount,
11132
11153
  captureMode,
11133
11154
  // Device-pixel box each video's frames were extracted to cover; a video not listed kept full size.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": "0.1.0",
3
3
  "buildId": "dev",
4
- "sha256": "3af46e671fc1165bd676de2c1e16493105979083a615954aa23ef784de92f7ce",
4
+ "sha256": "72ff9fd22c5222e47fb7ec1b9bb2e3643440846a146df5ced666c818e2027d86",
5
5
  "artifacts": {
6
6
  "iife": "hyperframe.runtime.iife.js",
7
7
  "esm": "hyperframe.runtime.mjs"