@hyperframes/producer 0.8.222 → 0.8.224

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/index.js CHANGED
@@ -4884,6 +4884,7 @@ async function validateCompletedSegments(manifest, expectedHash, probeFrames) {
4884
4884
  import {
4885
4885
  analyzeCompositionHdr,
4886
4886
  formatHdrAutoPromotionWarning,
4887
+ gradingKeepsSdr,
4887
4888
  HDR_AUTO_PROMOTION_PIPELINE,
4888
4889
  isHdrColorSpace,
4889
4890
  sanitizeHdrAutoPromotionAsset
@@ -4901,13 +4902,28 @@ function findRenderHdrAutoPromotionTrigger(input) {
4901
4902
  }
4902
4903
  return input.images.find(({ id }) => input.nativeHdrImageIds.has(id))?.src;
4903
4904
  }
4904
- function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources) {
4905
+ function logGradingResolution(input, hdrMode, effectiveHdr, hasHdrSource) {
4906
+ const keptSdr = hasHdrSource && gradingKeepsSdr(hdrMode, input.media);
4907
+ if (keptSdr) {
4908
+ input.log.warn(
4909
+ "[Render] HDR source detected, but color grading is SDR-only, so rendering SDR: HDR sources are tone-mapped before the grade. Use --hdr to force HDR output (grades stay SDR)."
4910
+ );
4911
+ } else if (effectiveHdr && [...input.media.videos, ...input.media.images].some((m) => m.graded)) {
4912
+ input.log.warn(
4913
+ "[Render] HDR output with graded media: color grading is SDR-only, so graded elements are graded in SDR and placed in the HDR output."
4914
+ );
4915
+ }
4916
+ return keptSdr;
4917
+ }
4918
+ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources, hasHdrSource) {
4919
+ const keptSdrForGrading = logGradingResolution(input, hdrMode, effectiveHdr, hasHdrSource);
4905
4920
  if (forcedHdrWithoutSources) {
4906
4921
  input.log.warn(
4907
4922
  "[Render] HDR forced by --hdr flag, but no HDR sources were detected, defaulting to HLG. SDR-only compositions may look perceptually wrong on HDR displays."
4908
4923
  );
4909
4924
  }
4910
4925
  if (!effectiveHdr) {
4926
+ if (keptSdrForGrading) return;
4911
4927
  input.log.info(
4912
4928
  hdrMode === "force-sdr" ? "[Render] SDR forced by --sdr flag" : "[Render] No HDR sources detected, rendering SDR"
4913
4929
  );
@@ -4932,8 +4948,8 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4932
4948
  function formatCarriesHdr(format) {
4933
4949
  return format === "mp4";
4934
4950
  }
4935
- function rendersSdrOnly(config) {
4936
- return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
4951
+ function rendersSdrOnly(config, media) {
4952
+ return config.hdrMode === "force-sdr" || gradingKeepsSdr(config.hdrMode, media) || !formatCarriesHdr(config.format ?? "mp4");
4937
4953
  }
4938
4954
  function resolveEffectiveHdrMode(input) {
4939
4955
  const hdrMode = input.hdrMode ?? "auto";
@@ -4944,7 +4960,7 @@ function resolveEffectiveHdrMode(input) {
4944
4960
  const info = allColorSpaces.length > 0 ? analyzeCompositionHdr(allColorSpaces) : null;
4945
4961
  let effectiveHdr;
4946
4962
  let forcedHdrWithoutSources = false;
4947
- if (hdrMode === "force-sdr") {
4963
+ if (hdrMode === "force-sdr" || gradingKeepsSdr(hdrMode, input.media)) {
4948
4964
  effectiveHdr = void 0;
4949
4965
  } else if (hdrMode === "force-hdr") {
4950
4966
  if (info?.hasHdr && info.dominantTransfer) {
@@ -4960,7 +4976,7 @@ function resolveEffectiveHdrMode(input) {
4960
4976
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4961
4977
  effectiveHdr = void 0;
4962
4978
  }
4963
- logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources);
4979
+ logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources, info?.hasHdr === true);
4964
4980
  return effectiveHdr;
4965
4981
  }
4966
4982
 
@@ -5825,6 +5841,7 @@ import {
5825
5841
  } from "@hyperframes/core/media-timing";
5826
5842
  import {
5827
5843
  parseVideoElements,
5844
+ hasGradedCanvas,
5828
5845
  parseImageElements,
5829
5846
  parseAudioElements,
5830
5847
  isSelfOrAncestorHidden,
@@ -5959,7 +5976,7 @@ function collectRenderMedia(html) {
5959
5976
  if (placed) audios.push(placed);
5960
5977
  else droppedMediaIds.add(elementId);
5961
5978
  }
5962
- return { videos, audios, images, droppedMediaIds };
5979
+ return { videos, audios, images, droppedMediaIds, gradedCanvas: hasGradedCanvas(html) };
5963
5980
  }
5964
5981
  function placeAudio(audio, window2) {
5965
5982
  const authoredEnd = audio.end > 0 ? audio.end : Infinity;
@@ -8014,7 +8031,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
8014
8031
  for (const [relPath, absPath] of preparedHeifAssets) {
8015
8032
  externalAssets.set(relPath, absPath);
8016
8033
  }
8017
- const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
8034
+ const { videos, audios, images, droppedMediaIds, gradedCanvas } = collectRenderMedia(html);
8018
8035
  const masterFxChain = parseMasterFxChain(html);
8019
8036
  const advised = /* @__PURE__ */ new Set();
8020
8037
  for (const video of videos) {
@@ -8053,6 +8070,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
8053
8070
  audios,
8054
8071
  images,
8055
8072
  droppedMediaIds,
8073
+ gradedCanvas,
8056
8074
  ...masterFxChain ? { masterFxChain } : {},
8057
8075
  unresolvedCompositions,
8058
8076
  externalAssets,
@@ -8506,6 +8524,7 @@ async function runCompileStage(input) {
8506
8524
  videos: compiled.videos,
8507
8525
  audios: compiled.audios,
8508
8526
  images: compiled.images,
8527
+ gradedCanvas: compiled.gradedCanvas,
8509
8528
  masterFxChain: compiled.masterFxChain,
8510
8529
  width: compiled.width,
8511
8530
  height: compiled.height
@@ -9284,6 +9303,7 @@ import {
9284
9303
  detectTransfer,
9285
9304
  extractAllVideoFrames,
9286
9305
  extractMediaMetadata as extractMediaMetadata3,
9306
+ gradingKeepsSdr as gradingKeepsSdr2,
9287
9307
  isVideoPastTimelineEnd,
9288
9308
  isHdrColorSpace as isHdrColorSpace2,
9289
9309
  rebaseVideoToWindow,
@@ -9524,10 +9544,11 @@ async function runExtractVideosStage(input) {
9524
9544
  let failureToEnforce = null;
9525
9545
  let videoReadinessSkipIds = [];
9526
9546
  let videoMetadataHints = [];
9547
+ const probesHdr = job.config.hdrMode !== "force-sdr" && !gradingKeepsSdr2(job.config.hdrMode, composition);
9527
9548
  const nativeHdrVideoIds = /* @__PURE__ */ new Set();
9528
9549
  const videoTransfers = /* @__PURE__ */ new Map();
9529
9550
  let hdrProbeTransientRetries = 0;
9530
- if (job.config.hdrMode !== "force-sdr" && composition.videos.length > 0) {
9551
+ if (probesHdr && composition.videos.length > 0) {
9531
9552
  log?.info("Probing video color spaces...", { videoCount: composition.videos.length });
9532
9553
  const probeFailures = await Promise.all(
9533
9554
  composition.videos.map(async (v) => {
@@ -9566,7 +9587,7 @@ async function runExtractVideosStage(input) {
9566
9587
  const imageTransfers = /* @__PURE__ */ new Map();
9567
9588
  const hdrImageSrcPaths = /* @__PURE__ */ new Map();
9568
9589
  const imageColorSpaces = [];
9569
- if (job.config.hdrMode !== "force-sdr" && composition.images.length > 0) {
9590
+ if (probesHdr && composition.images.length > 0) {
9570
9591
  const probed = await Promise.all(
9571
9592
  composition.images.map(async (img) => {
9572
9593
  const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
@@ -9603,7 +9624,7 @@ async function runExtractVideosStage(input) {
9603
9624
  fps: job.config.fps,
9604
9625
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9605
9626
  format: job.config.videoFrameFormat ?? "auto",
9606
- toneMapHdrToSdr: rendersSdrOnly(job.config),
9627
+ toneMapHdrToSdr: rendersSdrOnly(job.config, composition),
9607
9628
  timelineEnd: composition.duration,
9608
9629
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9609
9630
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -11361,6 +11382,7 @@ async function runCaptureHdrStage(input) {
11361
11382
  let lastBrowserConsole = [];
11362
11383
  let captureDurationMs = 0;
11363
11384
  let encodeMs = 0;
11385
+ let encoderDrainWaitMs;
11364
11386
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
11365
11387
  log.info(
11366
11388
  hasHdrContent ? "[Render] HDR layered composite: z-ordered DOM + native HDR video/image layers" : "[Render] Shader transition composite: z-ordered SDR DOM layers"
@@ -11598,6 +11620,7 @@ async function runCaptureHdrStage(input) {
11598
11620
  reportEncodeProgress(job, totalFrames, totalFrames, onProgress, encodeFrom);
11599
11621
  captureDurationMs = Date.now() - stageStart;
11600
11622
  encodeMs = hdrEncodeResult.durationMs;
11623
+ encoderDrainWaitMs = hdrEncodeResult.drainWaitMs;
11601
11624
  } catch (error) {
11602
11625
  lastBrowserConsole = domSession.browserConsoleBuffer;
11603
11626
  throw wrapCaptureStageError(error, lastBrowserConsole);
@@ -11630,6 +11653,7 @@ async function runCaptureHdrStage(input) {
11630
11653
  hdrPerf,
11631
11654
  captureDurationMs,
11632
11655
  encodeMs,
11656
+ encoderDrainWaitMs,
11633
11657
  warnings: cloneCaptureWarnings2(domSession.warnings)
11634
11658
  };
11635
11659
  }
@@ -13529,6 +13553,7 @@ async function executeRenderPipeline(input) {
13529
13553
  });
13530
13554
  const effectiveHdr = resolveEffectiveHdrMode({
13531
13555
  hdrMode: job.config.hdrMode,
13556
+ media: composition,
13532
13557
  outputFormat,
13533
13558
  extractionResult,
13534
13559
  imageColorSpaces,
@@ -14343,6 +14368,7 @@ async function executeRenderPipeline(input) {
14343
14368
  perfStages.captureFrameMs = hdrRes.captureDurationMs;
14344
14369
  perfStages.captureSetupMs = Math.max(0, Date.now() - stage4Start - hdrRes.captureDurationMs);
14345
14370
  perfStages.encodeMs = hdrRes.encodeMs;
14371
+ perfStages.encoderDrainWaitMs = hdrRes.encoderDrainWaitMs ?? 0;
14346
14372
  } else {
14347
14373
  let streamingHandled = false;
14348
14374
  if (capturePlan.kind === "sdr_segmented") {
@@ -4901,6 +4901,7 @@ async function validateCompletedSegments(manifest, expectedHash, probeFrames) {
4901
4901
  import {
4902
4902
  analyzeCompositionHdr,
4903
4903
  formatHdrAutoPromotionWarning,
4904
+ gradingKeepsSdr,
4904
4905
  HDR_AUTO_PROMOTION_PIPELINE,
4905
4906
  isHdrColorSpace,
4906
4907
  sanitizeHdrAutoPromotionAsset
@@ -4918,13 +4919,28 @@ function findRenderHdrAutoPromotionTrigger(input) {
4918
4919
  }
4919
4920
  return input.images.find(({ id }) => input.nativeHdrImageIds.has(id))?.src;
4920
4921
  }
4921
- function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources) {
4922
+ function logGradingResolution(input, hdrMode, effectiveHdr, hasHdrSource) {
4923
+ const keptSdr = hasHdrSource && gradingKeepsSdr(hdrMode, input.media);
4924
+ if (keptSdr) {
4925
+ input.log.warn(
4926
+ "[Render] HDR source detected, but color grading is SDR-only, so rendering SDR: HDR sources are tone-mapped before the grade. Use --hdr to force HDR output (grades stay SDR)."
4927
+ );
4928
+ } else if (effectiveHdr && [...input.media.videos, ...input.media.images].some((m) => m.graded)) {
4929
+ input.log.warn(
4930
+ "[Render] HDR output with graded media: color grading is SDR-only, so graded elements are graded in SDR and placed in the HDR output."
4931
+ );
4932
+ }
4933
+ return keptSdr;
4934
+ }
4935
+ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources, hasHdrSource) {
4936
+ const keptSdrForGrading = logGradingResolution(input, hdrMode, effectiveHdr, hasHdrSource);
4922
4937
  if (forcedHdrWithoutSources) {
4923
4938
  input.log.warn(
4924
4939
  "[Render] HDR forced by --hdr flag, but no HDR sources were detected, defaulting to HLG. SDR-only compositions may look perceptually wrong on HDR displays."
4925
4940
  );
4926
4941
  }
4927
4942
  if (!effectiveHdr) {
4943
+ if (keptSdrForGrading) return;
4928
4944
  input.log.info(
4929
4945
  hdrMode === "force-sdr" ? "[Render] SDR forced by --sdr flag" : "[Render] No HDR sources detected, rendering SDR"
4930
4946
  );
@@ -4949,8 +4965,8 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4949
4965
  function formatCarriesHdr(format) {
4950
4966
  return format === "mp4";
4951
4967
  }
4952
- function rendersSdrOnly(config) {
4953
- return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
4968
+ function rendersSdrOnly(config, media) {
4969
+ return config.hdrMode === "force-sdr" || gradingKeepsSdr(config.hdrMode, media) || !formatCarriesHdr(config.format ?? "mp4");
4954
4970
  }
4955
4971
  function resolveEffectiveHdrMode(input) {
4956
4972
  const hdrMode = input.hdrMode ?? "auto";
@@ -4961,7 +4977,7 @@ function resolveEffectiveHdrMode(input) {
4961
4977
  const info = allColorSpaces.length > 0 ? analyzeCompositionHdr(allColorSpaces) : null;
4962
4978
  let effectiveHdr;
4963
4979
  let forcedHdrWithoutSources = false;
4964
- if (hdrMode === "force-sdr") {
4980
+ if (hdrMode === "force-sdr" || gradingKeepsSdr(hdrMode, input.media)) {
4965
4981
  effectiveHdr = void 0;
4966
4982
  } else if (hdrMode === "force-hdr") {
4967
4983
  if (info?.hasHdr && info.dominantTransfer) {
@@ -4977,7 +4993,7 @@ function resolveEffectiveHdrMode(input) {
4977
4993
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4978
4994
  effectiveHdr = void 0;
4979
4995
  }
4980
- logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources);
4996
+ logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources, info?.hasHdr === true);
4981
4997
  return effectiveHdr;
4982
4998
  }
4983
4999
 
@@ -5842,6 +5858,7 @@ import {
5842
5858
  } from "@hyperframes/core/media-timing";
5843
5859
  import {
5844
5860
  parseVideoElements,
5861
+ hasGradedCanvas,
5845
5862
  parseImageElements,
5846
5863
  parseAudioElements,
5847
5864
  isSelfOrAncestorHidden,
@@ -5976,7 +5993,7 @@ function collectRenderMedia(html) {
5976
5993
  if (placed) audios.push(placed);
5977
5994
  else droppedMediaIds.add(elementId);
5978
5995
  }
5979
- return { videos, audios, images, droppedMediaIds };
5996
+ return { videos, audios, images, droppedMediaIds, gradedCanvas: hasGradedCanvas(html) };
5980
5997
  }
5981
5998
  function placeAudio(audio, window2) {
5982
5999
  const authoredEnd = audio.end > 0 ? audio.end : Infinity;
@@ -8031,7 +8048,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
8031
8048
  for (const [relPath, absPath] of preparedHeifAssets) {
8032
8049
  externalAssets.set(relPath, absPath);
8033
8050
  }
8034
- const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
8051
+ const { videos, audios, images, droppedMediaIds, gradedCanvas } = collectRenderMedia(html);
8035
8052
  const masterFxChain = parseMasterFxChain(html);
8036
8053
  const advised = /* @__PURE__ */ new Set();
8037
8054
  for (const video of videos) {
@@ -8070,6 +8087,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
8070
8087
  audios,
8071
8088
  images,
8072
8089
  droppedMediaIds,
8090
+ gradedCanvas,
8073
8091
  ...masterFxChain ? { masterFxChain } : {},
8074
8092
  unresolvedCompositions,
8075
8093
  externalAssets,
@@ -8523,6 +8541,7 @@ async function runCompileStage(input) {
8523
8541
  videos: compiled.videos,
8524
8542
  audios: compiled.audios,
8525
8543
  images: compiled.images,
8544
+ gradedCanvas: compiled.gradedCanvas,
8526
8545
  masterFxChain: compiled.masterFxChain,
8527
8546
  width: compiled.width,
8528
8547
  height: compiled.height
@@ -9269,6 +9288,7 @@ import {
9269
9288
  detectTransfer,
9270
9289
  extractAllVideoFrames,
9271
9290
  extractMediaMetadata as extractMediaMetadata3,
9291
+ gradingKeepsSdr as gradingKeepsSdr2,
9272
9292
  isVideoPastTimelineEnd,
9273
9293
  isHdrColorSpace as isHdrColorSpace2,
9274
9294
  rebaseVideoToWindow,
@@ -9504,10 +9524,11 @@ async function runExtractVideosStage(input) {
9504
9524
  let failureToEnforce = null;
9505
9525
  let videoReadinessSkipIds = [];
9506
9526
  let videoMetadataHints = [];
9527
+ const probesHdr = job.config.hdrMode !== "force-sdr" && !gradingKeepsSdr2(job.config.hdrMode, composition);
9507
9528
  const nativeHdrVideoIds = /* @__PURE__ */ new Set();
9508
9529
  const videoTransfers = /* @__PURE__ */ new Map();
9509
9530
  let hdrProbeTransientRetries = 0;
9510
- if (job.config.hdrMode !== "force-sdr" && composition.videos.length > 0) {
9531
+ if (probesHdr && composition.videos.length > 0) {
9511
9532
  log?.info("Probing video color spaces...", { videoCount: composition.videos.length });
9512
9533
  const probeFailures = await Promise.all(
9513
9534
  composition.videos.map(async (v) => {
@@ -9546,7 +9567,7 @@ async function runExtractVideosStage(input) {
9546
9567
  const imageTransfers = /* @__PURE__ */ new Map();
9547
9568
  const hdrImageSrcPaths = /* @__PURE__ */ new Map();
9548
9569
  const imageColorSpaces = [];
9549
- if (job.config.hdrMode !== "force-sdr" && composition.images.length > 0) {
9570
+ if (probesHdr && composition.images.length > 0) {
9550
9571
  const probed = await Promise.all(
9551
9572
  composition.images.map(async (img) => {
9552
9573
  const imgPath = resolveProjectRelativeSrc3(img.src, projectDir, compiledDir);
@@ -9583,7 +9604,7 @@ async function runExtractVideosStage(input) {
9583
9604
  fps: job.config.fps,
9584
9605
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9585
9606
  format: job.config.videoFrameFormat ?? "auto",
9586
- toneMapHdrToSdr: rendersSdrOnly(job.config),
9607
+ toneMapHdrToSdr: rendersSdrOnly(job.config, composition),
9587
9608
  timelineEnd: composition.duration,
9588
9609
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9589
9610
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -11341,6 +11362,7 @@ async function runCaptureHdrStage(input) {
11341
11362
  let lastBrowserConsole = [];
11342
11363
  let captureDurationMs = 0;
11343
11364
  let encodeMs = 0;
11365
+ let encoderDrainWaitMs;
11344
11366
  const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
11345
11367
  log.info(
11346
11368
  hasHdrContent ? "[Render] HDR layered composite: z-ordered DOM + native HDR video/image layers" : "[Render] Shader transition composite: z-ordered SDR DOM layers"
@@ -11578,6 +11600,7 @@ async function runCaptureHdrStage(input) {
11578
11600
  reportEncodeProgress(job, totalFrames, totalFrames, onProgress, encodeFrom);
11579
11601
  captureDurationMs = Date.now() - stageStart;
11580
11602
  encodeMs = hdrEncodeResult.durationMs;
11603
+ encoderDrainWaitMs = hdrEncodeResult.drainWaitMs;
11581
11604
  } catch (error) {
11582
11605
  lastBrowserConsole = domSession.browserConsoleBuffer;
11583
11606
  throw wrapCaptureStageError(error, lastBrowserConsole);
@@ -11610,6 +11633,7 @@ async function runCaptureHdrStage(input) {
11610
11633
  hdrPerf,
11611
11634
  captureDurationMs,
11612
11635
  encodeMs,
11636
+ encoderDrainWaitMs,
11613
11637
  warnings: cloneCaptureWarnings2(domSession.warnings)
11614
11638
  };
11615
11639
  }
@@ -13509,6 +13533,7 @@ async function executeRenderPipeline(input) {
13509
13533
  });
13510
13534
  const effectiveHdr = resolveEffectiveHdrMode({
13511
13535
  hdrMode: job.config.hdrMode,
13536
+ media: composition,
13512
13537
  outputFormat,
13513
13538
  extractionResult,
13514
13539
  imageColorSpaces,
@@ -14323,6 +14348,7 @@ async function executeRenderPipeline(input) {
14323
14348
  perfStages.captureFrameMs = hdrRes.captureDurationMs;
14324
14349
  perfStages.captureSetupMs = Math.max(0, Date.now() - stage4Start - hdrRes.captureDurationMs);
14325
14350
  perfStages.encodeMs = hdrRes.encodeMs;
14351
+ perfStages.encoderDrainWaitMs = hdrRes.encoderDrainWaitMs ?? 0;
14326
14352
  } else {
14327
14353
  let streamingHandled = false;
14328
14354
  if (capturePlan.kind === "sdr_segmented") {
@@ -9,6 +9,7 @@ export interface CompiledComposition {
9
9
  audios: AudioElement[];
10
10
  images: ImageElement[];
11
11
  droppedMediaIds: Set<string>;
12
+ gradedCanvas: boolean;
12
13
  masterFxChain?: string;
13
14
  unresolvedCompositions: UnresolvedElement[];
14
15
  /** Assets that resolve outside projectDir. Keys are the path used in HTML, values are absolute filesystem paths. */
@@ -8,14 +8,17 @@
8
8
  * SDR) lives here too so the sequencer doesn't need to know which
9
9
  * formats can carry an HDR signal.
10
10
  */
11
+ import { gradingKeepsSdr } from "@hyperframes/engine";
11
12
  import type { ExtractionResult, HdrTransfer, VideoColorSpace } from "@hyperframes/engine";
12
13
  import type { ProducerLogger } from "../../logger.js";
13
14
  import type { RenderConfig } from "../renderOrchestrator.js";
14
15
  type EffectiveHdr = {
15
16
  transfer: HdrTransfer;
16
17
  } | undefined;
18
+ type RenderMedia = Parameters<typeof gradingKeepsSdr>[1];
17
19
  type HdrModeInput = {
18
20
  hdrMode: RenderConfig["hdrMode"];
21
+ media: RenderMedia;
19
22
  outputFormat: NonNullable<RenderConfig["format"]>;
20
23
  extractionResult: ExtractionResult | null | undefined;
21
24
  imageColorSpaces: (VideoColorSpace | null)[];
@@ -34,6 +37,6 @@ export declare function findRenderHdrAutoPromotionTrigger(input: {
34
37
  }[];
35
38
  nativeHdrImageIds: ReadonlySet<string>;
36
39
  }): string | undefined;
37
- export declare function rendersSdrOnly(config: Pick<RenderConfig, "hdrMode" | "format">): boolean;
40
+ export declare function rendersSdrOnly(config: Pick<RenderConfig, "hdrMode" | "format">, media: RenderMedia): boolean;
38
41
  export declare function resolveEffectiveHdrMode(input: HdrModeInput): EffectiveHdr;
39
42
  export {};
@@ -19,6 +19,7 @@ export interface CompositionMetadata {
19
19
  videos: VideoElement[];
20
20
  audios: AudioElement[];
21
21
  images: ImageElement[];
22
+ gradedCanvas?: boolean;
22
23
  masterFxChain?: string;
23
24
  width: number;
24
25
  height: number;
@@ -84,6 +84,7 @@ export interface CaptureHdrStageResult {
84
84
  captureDurationMs: number;
85
85
  /** ffmpeg-reported encode duration; overlapped with capture. */
86
86
  encodeMs: number;
87
+ encoderDrainWaitMs?: number;
87
88
  warnings: CaptureWarning[];
88
89
  }
89
90
  export declare function runCaptureHdrStage(input: CaptureHdrStageInput): Promise<CaptureHdrStageResult>;
@@ -16,7 +16,7 @@
16
16
  * - `perfStages.videoExtractMs` is set at the same end-of-stage point.
17
17
  * - `materializeExtractedFramesForCompiledDir` is still called once
18
18
  * when `extractionResult.extracted` is non-empty.
19
- * - `force-sdr` mode still skips ALL ffprobe overhead.
19
+ * - `force-sdr` mode, and auto mode with graded media, still skip ALL ffprobe overhead.
20
20
  *
21
21
  * New for distributed mode:
22
22
  * - `materializeSymlinks` (default `false`) — when `true`, the stage
@@ -14,6 +14,7 @@ export interface RenderMedia {
14
14
  images: ImageElement[];
15
15
  /** Ids of clips in the document that never play; the browser probe must not add them back. */
16
16
  droppedMediaIds: Set<string>;
17
+ gradedCanvas: boolean;
17
18
  }
18
19
  /**
19
20
  * Parse every media element in the inlined render document, with each clip's
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.222",
3
+ "version": "0.8.224",
4
4
  "description": "HTML-to-video rendering engine using Chrome's BeginFrame API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,11 +35,11 @@
35
35
  "postcss": "^8.4.0",
36
36
  "puppeteer": "~25.11.0",
37
37
  "puppeteer-core": "^25.11.0",
38
- "@hyperframes/engine": "0.8.222",
39
- "@hyperframes/studio-server": "0.8.222",
40
- "@hyperframes/core": "0.8.222",
41
- "@hyperframes/lint": "0.8.222",
42
- "@hyperframes/parsers": "0.8.222"
38
+ "@hyperframes/lint": "0.8.224",
39
+ "@hyperframes/engine": "0.8.224",
40
+ "@hyperframes/studio-server": "0.8.224",
41
+ "@hyperframes/core": "0.8.224",
42
+ "@hyperframes/parsers": "0.8.224"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^25.0.10",
@@ -48,7 +48,7 @@
48
48
  "tsx": "^4.21.0",
49
49
  "typescript": "^5.7.2",
50
50
  "vitest": "^4.1.11",
51
- "@hyperframes/aws-lambda": "0.8.222"
51
+ "@hyperframes/aws-lambda": "0.8.224"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"