@hyperframes/producer 0.8.208 → 0.8.210

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
@@ -775,7 +775,7 @@ function createFileServer(options) {
775
775
  if (!streamOwnsFd) closeSync(fd);
776
776
  }
777
777
  });
778
- return new Promise((resolve13) => {
778
+ return new Promise((resolve13, reject) => {
779
779
  const connections = /* @__PURE__ */ new Set();
780
780
  const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
781
781
  resolve13({
@@ -795,6 +795,7 @@ function createFileServer(options) {
795
795
  connections.add(socket);
796
796
  socket.on("close", () => connections.delete(socket));
797
797
  });
798
+ server.on("error", reject);
798
799
  });
799
800
  }
800
801
 
@@ -846,9 +847,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
846
847
  }
847
848
 
848
849
  // src/services/render/deliveryEncoder.ts
849
- import { appendRateCapArgs, keyframeIntervalToGopSize as keyframeIntervalToGopSize2 } from "@hyperframes/engine";
850
+ import {
851
+ PRORES_PROFILE_NAMES,
852
+ appendRateCapArgs,
853
+ isProresProfile,
854
+ keyframeIntervalToGopSize as keyframeIntervalToGopSize2
855
+ } from "@hyperframes/engine";
856
+ function validateProresProfile(format, proresProfile) {
857
+ if (proresProfile === void 0) return;
858
+ if (!isProresProfile(proresProfile)) {
859
+ throw new Error(
860
+ `proresProfile must be one of ${PRORES_PROFILE_NAMES.join(", ")}; got ${JSON.stringify(proresProfile)}`
861
+ );
862
+ }
863
+ if (format !== void 0 && format !== "mov") {
864
+ throw new Error(`proresProfile applies to mov renders, not ${format}`);
865
+ }
866
+ }
850
867
  function validateDeliveryEncoderConfig(config) {
851
- const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
868
+ const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
869
+ validateProresProfile(format, proresProfile);
852
870
  if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
853
871
  return;
854
872
  if (keyframeIntervalSeconds !== void 0) {
@@ -3229,14 +3247,15 @@ async function extractHdrVideoFrames(args) {
3229
3247
  }
3230
3248
  first.release();
3231
3249
  if (video.loop) (await windows.lease(totalFrames - 1))?.release();
3250
+ const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
3232
3251
  out.set(videoId, {
3233
3252
  dir: frameDir,
3234
- windows,
3253
+ windows: held,
3235
3254
  width: dims.width,
3236
3255
  height: dims.height,
3237
3256
  frameSize,
3238
3257
  get frameCount() {
3239
- return windows.totalFrames;
3258
+ return held.totalFrames;
3240
3259
  },
3241
3260
  scratch: Buffer.allocUnsafe(frameSize),
3242
3261
  loop: video.loop,
@@ -3265,9 +3284,18 @@ function hdrWindowFfmpegArgs(a) {
3265
3284
  if (trim > 0) filters.push(`trim=start_frame=${trim}`);
3266
3285
  return [
3267
3286
  ...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
3268
- ...["-vf", filters.join(","), "-frames:v", String(a.frames)]
3287
+ ...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
3269
3288
  ];
3270
3289
  }
3290
+ function holdFirstFrameUntilStreamStarts(windows, firstTick) {
3291
+ return {
3292
+ get totalFrames() {
3293
+ return firstTick + windows.totalFrames;
3294
+ },
3295
+ lease: (frameIndex) => windows.lease(Math.max(0, frameIndex - firstTick)),
3296
+ close: () => windows.close()
3297
+ };
3298
+ }
3271
3299
  function decodeHdrImageBuffers(args) {
3272
3300
  const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
3273
3301
  const out = /* @__PURE__ */ new Map();
@@ -4833,6 +4861,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4833
4861
  `[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
4834
4862
  );
4835
4863
  }
4864
+ function formatCarriesHdr(format) {
4865
+ return format === "mp4";
4866
+ }
4867
+ function rendersSdrOnly(config) {
4868
+ return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
4869
+ }
4836
4870
  function resolveEffectiveHdrMode(input) {
4837
4871
  const hdrMode = input.hdrMode ?? "auto";
4838
4872
  const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
@@ -4854,7 +4888,7 @@ function resolveEffectiveHdrMode(input) {
4854
4888
  } else if (info?.hasHdr && info.dominantTransfer) {
4855
4889
  effectiveHdr = { transfer: info.dominantTransfer };
4856
4890
  }
4857
- if (effectiveHdr && input.outputFormat !== "mp4") {
4891
+ if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
4858
4892
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4859
4893
  effectiveHdr = void 0;
4860
4894
  }
@@ -5668,7 +5702,7 @@ import { gsapCdnDist, normalizeGsapBaseUrl } from "@hyperframes/core/gsap-cdn";
5668
5702
  import {
5669
5703
  assignBundledRuntimeCompositionIds,
5670
5704
  assignMediaRenderIds,
5671
- buildVariablesByCompScript,
5705
+ emitVariablesByCompScript,
5672
5706
  inlineSubCompositions as inlineSubCompositionsShared,
5673
5707
  ensureExternalLinkTag,
5674
5708
  ensureExternalScriptTag,
@@ -7240,9 +7274,9 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
7240
7274
  }
7241
7275
  }
7242
7276
  }
7243
- const variablesByCompScript = buildVariablesByCompScript(result.variablesByComp);
7277
+ emitVariablesByCompScript(document2, result.variablesByComp);
7244
7278
  if (body) {
7245
- let pending = variablesByCompScript ? [variablesByCompScript] : [];
7279
+ let pending = [];
7246
7280
  const flushInline = () => {
7247
7281
  if (!pending.length) return;
7248
7282
  const joined = pending.join("\n;\n");
@@ -9476,7 +9510,7 @@ async function runExtractVideosStage(input) {
9476
9510
  fps: job.config.fps,
9477
9511
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9478
9512
  format: job.config.videoFrameFormat ?? "auto",
9479
- toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
9513
+ toneMapHdrToSdr: rendersSdrOnly(job.config),
9480
9514
  timelineEnd: composition.duration,
9481
9515
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9482
9516
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -14060,7 +14094,12 @@ async function executeRenderPipeline(input) {
14060
14094
  });
14061
14095
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
14062
14096
  const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
14063
- const preset = getEncoderPreset3(job.config.quality, presetFormat, encoderHdr);
14097
+ const preset = getEncoderPreset3(
14098
+ job.config.quality,
14099
+ presetFormat,
14100
+ encoderHdr,
14101
+ job.config.proresProfile
14102
+ );
14064
14103
  if (job.config.crf != null && job.config.videoBitrate) {
14065
14104
  log.warn(
14066
14105
  `[Render] Both crf=${job.config.crf} and videoBitrate=${job.config.videoBitrate} were set. These are mutually exclusive; honoring crf and ignoring videoBitrate. Set only one to silence this warning.`
@@ -14875,6 +14914,7 @@ async function executeRenderPipeline(input) {
14875
14914
 
14876
14915
  // src/renderRequest.ts
14877
14916
  import {
14917
+ PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
14878
14918
  isVideoFrameFormat as isVideoFrameFormat2,
14879
14919
  resolveConfig as resolveConfig4,
14880
14920
  validateEngineConfigSnapshot as validateEngineConfigSnapshot2
@@ -16892,6 +16932,8 @@ function buildLockedRenderConfig(input) {
16892
16932
  closedGop: true,
16893
16933
  forceKeyframes: "n=0",
16894
16934
  pixelFormat,
16935
+ // Omitted for the default so existing 4444 plans keep their planHash.
16936
+ ...config.proresProfile && config.proresProfile !== "4444" ? { proresProfile: config.proresProfile } : {},
16895
16937
  chunkSize: input.effectiveChunkSize,
16896
16938
  chunkCount: input.chunkCount,
16897
16939
  runtimeEnv: input.runtimeEnv,
@@ -16913,7 +16955,11 @@ function resolveEncoderTriple(config) {
16913
16955
  `[plan] DistributedRenderConfig.codec is only valid for format="mp4"; received codec=${JSON.stringify(config.codec)} with format=${JSON.stringify(config.format)}. Omit codec for non-mp4 formats: mov is always ProRes 4444, webm is always libvpx-vp9, and png-sequence has no encoder.`
16914
16956
  );
16915
16957
  }
16916
- return resolveNonMp4EncoderTriple(config.format, config.quality ?? "standard");
16958
+ return resolveNonMp4EncoderTriple(
16959
+ config.format,
16960
+ config.quality ?? "standard",
16961
+ config.proresProfile
16962
+ );
16917
16963
  }
16918
16964
  function resolveMp4EncoderTriple(codec) {
16919
16965
  const c = codec ?? "h264";
@@ -16927,9 +16973,10 @@ function resolveMp4EncoderTriple(codec) {
16927
16973
  }
16928
16974
  return { encoder: "libx264-software", pixelFormat: "yuv420p", preset: "medium" };
16929
16975
  }
16930
- function resolveNonMp4EncoderTriple(format, quality) {
16976
+ function resolveNonMp4EncoderTriple(format, quality, proresProfile) {
16931
16977
  if (format === "mov") {
16932
- return { encoder: "prores-software", pixelFormat: "yuva444p10le", preset: "4444" };
16978
+ const { pixelFormat, preset } = getEncoderPreset4(quality, "mov", void 0, proresProfile);
16979
+ return { encoder: "prores-software", pixelFormat, preset };
16933
16980
  }
16934
16981
  if (format === "webm") {
16935
16982
  const enginePreset = getEncoderPreset4(quality, "webm");
@@ -17584,6 +17631,18 @@ function validateDistributedRenderConfig(config) {
17584
17631
  error instanceof Error ? error.message : String(error)
17585
17632
  );
17586
17633
  }
17634
+ try {
17635
+ validateDeliveryEncoderConfig({
17636
+ format: config.format,
17637
+ fps: { num: config.fps, den: 1 },
17638
+ proresProfile: config.proresProfile
17639
+ });
17640
+ } catch (error) {
17641
+ throw new InvalidConfigError(
17642
+ "config.proresProfile",
17643
+ error instanceof Error ? error.message : String(error)
17644
+ );
17645
+ }
17587
17646
  if (config.chunkSize !== void 0) {
17588
17647
  if (!Number.isInteger(config.chunkSize) || config.chunkSize < 1) {
17589
17648
  throw new InvalidConfigError(
@@ -17823,6 +17882,7 @@ function assertRequestOptionScalars(options) {
17823
17882
  for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
17824
17883
  assertOptionalString(options, field);
17825
17884
  }
17885
+ assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
17826
17886
  assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
17827
17887
  assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
17828
17888
  assertOptionalEnum(options, "pageMotionBlur", ["off"]);
@@ -17924,6 +17984,7 @@ function distributedConfigFromRequest(request, runtime = {}) {
17924
17984
  pageMotionBlur: options.pageMotionBlur,
17925
17985
  bitrate: options.videoBitrate,
17926
17986
  keyframeIntervalSeconds: options.keyframeIntervalSeconds,
17987
+ proresProfile: options.proresProfile,
17927
17988
  maxBitrate: options.maxBitrate,
17928
17989
  bufferSize: options.bufferSize,
17929
17990
  videoFrameFormat: options.videoFrameFormat,
@@ -17974,6 +18035,7 @@ function renderRequestFromDistributedConfig(input) {
17974
18035
  ...optionalProperty("pageMotionBlur", config.pageMotionBlur),
17975
18036
  ...optionalProperty("videoBitrate", config.bitrate),
17976
18037
  ...optionalProperty("keyframeIntervalSeconds", config.keyframeIntervalSeconds),
18038
+ ...optionalProperty("proresProfile", config.proresProfile),
17977
18039
  ...optionalProperty("maxBitrate", config.maxBitrate),
17978
18040
  ...optionalProperty("bufferSize", config.bufferSize),
17979
18041
  ...optionalProperty("videoFrameFormat", config.videoFrameFormat),
@@ -20596,7 +20658,12 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath, options) {
20596
20658
  const isPngSequence = plan2.dimensions.format === "png-sequence";
20597
20659
  outputKind = isPngSequence ? "frame-dir" : "file";
20598
20660
  const presetFormat = isPngSequence ? "mp4" : plan2.dimensions.format;
20599
- const basePreset = getEncoderPreset6(job.config.quality, presetFormat, void 0);
20661
+ const basePreset = getEncoderPreset6(
20662
+ job.config.quality,
20663
+ presetFormat,
20664
+ void 0,
20665
+ encoder.proresProfile
20666
+ );
20600
20667
  const preset = resolvePresetForLockedEncoder(basePreset, encoder.encoder);
20601
20668
  const effectiveQuality = encoder.crf ?? preset.quality;
20602
20669
  const effectiveBitrate = encoder.crf != null ? void 0 : encoder.bitrate;
@@ -796,7 +796,7 @@ function createFileServer(options) {
796
796
  if (!streamOwnsFd) closeSync(fd);
797
797
  }
798
798
  });
799
- return new Promise((resolve11) => {
799
+ return new Promise((resolve11, reject) => {
800
800
  const connections = /* @__PURE__ */ new Set();
801
801
  const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
802
802
  resolve11({
@@ -816,6 +816,7 @@ function createFileServer(options) {
816
816
  connections.add(socket);
817
817
  socket.on("close", () => connections.delete(socket));
818
818
  });
819
+ server.on("error", reject);
819
820
  });
820
821
  }
821
822
 
@@ -867,9 +868,26 @@ function resolveHlsEncoderGopLock(outputFormat, fps, segmentSeconds) {
867
868
  }
868
869
 
869
870
  // src/services/render/deliveryEncoder.ts
870
- import { appendRateCapArgs, keyframeIntervalToGopSize as keyframeIntervalToGopSize2 } from "@hyperframes/engine";
871
+ import {
872
+ PRORES_PROFILE_NAMES,
873
+ appendRateCapArgs,
874
+ isProresProfile,
875
+ keyframeIntervalToGopSize as keyframeIntervalToGopSize2
876
+ } from "@hyperframes/engine";
877
+ function validateProresProfile(format, proresProfile) {
878
+ if (proresProfile === void 0) return;
879
+ if (!isProresProfile(proresProfile)) {
880
+ throw new Error(
881
+ `proresProfile must be one of ${PRORES_PROFILE_NAMES.join(", ")}; got ${JSON.stringify(proresProfile)}`
882
+ );
883
+ }
884
+ if (format !== void 0 && format !== "mov") {
885
+ throw new Error(`proresProfile applies to mov renders, not ${format}`);
886
+ }
887
+ }
871
888
  function validateDeliveryEncoderConfig(config) {
872
- const { keyframeIntervalSeconds, maxBitrate, bufferSize, format } = config;
889
+ const { keyframeIntervalSeconds, maxBitrate, bufferSize, format, proresProfile } = config;
890
+ validateProresProfile(format, proresProfile);
873
891
  if (keyframeIntervalSeconds === void 0 && maxBitrate === void 0 && bufferSize === void 0)
874
892
  return;
875
893
  if (keyframeIntervalSeconds !== void 0) {
@@ -3246,14 +3264,15 @@ async function extractHdrVideoFrames(args) {
3246
3264
  }
3247
3265
  first.release();
3248
3266
  if (video.loop) (await windows.lease(totalFrames - 1))?.release();
3267
+ const held = holdFirstFrameUntilStreamStarts(windows, firstTick);
3249
3268
  out.set(videoId, {
3250
3269
  dir: frameDir,
3251
- windows,
3270
+ windows: held,
3252
3271
  width: dims.width,
3253
3272
  height: dims.height,
3254
3273
  frameSize,
3255
3274
  get frameCount() {
3256
- return windows.totalFrames;
3275
+ return held.totalFrames;
3257
3276
  },
3258
3277
  scratch: Buffer.allocUnsafe(frameSize),
3259
3278
  loop: video.loop,
@@ -3282,9 +3301,18 @@ function hdrWindowFfmpegArgs(a) {
3282
3301
  if (trim > 0) filters.push(`trim=start_frame=${trim}`);
3283
3302
  return [
3284
3303
  ...["-ss", (a.mediaStart + seekFrame / a.fps).toFixed(6), "-i", a.srcPath],
3285
- ...["-vf", filters.join(","), "-frames:v", String(a.frames)]
3304
+ ...["-vf", filters.join(","), "-frames:v", String(a.frames), "-fps_mode", "passthrough"]
3286
3305
  ];
3287
3306
  }
3307
+ function holdFirstFrameUntilStreamStarts(windows, firstTick) {
3308
+ return {
3309
+ get totalFrames() {
3310
+ return firstTick + windows.totalFrames;
3311
+ },
3312
+ lease: (frameIndex) => windows.lease(Math.max(0, frameIndex - firstTick)),
3313
+ close: () => windows.close()
3314
+ };
3315
+ }
3288
3316
  function decodeHdrImageBuffers(args) {
3289
3317
  const { log, hdrImageSrcPaths, prep, hdrDiagnostics } = args;
3290
3318
  const out = /* @__PURE__ */ new Map();
@@ -4850,6 +4878,12 @@ function logHdrResolution(input, hdrMode, effectiveHdr, forcedHdrWithoutSources)
4850
4878
  `[Render] HDR ${reason}, output: ${effectiveHdr.transfer.toUpperCase()} (BT.2020, 10-bit H.265)`
4851
4879
  );
4852
4880
  }
4881
+ function formatCarriesHdr(format) {
4882
+ return format === "mp4";
4883
+ }
4884
+ function rendersSdrOnly(config) {
4885
+ return config.hdrMode === "force-sdr" || !formatCarriesHdr(config.format ?? "mp4");
4886
+ }
4853
4887
  function resolveEffectiveHdrMode(input) {
4854
4888
  const hdrMode = input.hdrMode ?? "auto";
4855
4889
  const videoColorSpaces = (input.extractionResult?.extracted ?? []).map(
@@ -4871,7 +4905,7 @@ function resolveEffectiveHdrMode(input) {
4871
4905
  } else if (info?.hasHdr && info.dominantTransfer) {
4872
4906
  effectiveHdr = { transfer: info.dominantTransfer };
4873
4907
  }
4874
- if (effectiveHdr && input.outputFormat !== "mp4") {
4908
+ if (effectiveHdr && !formatCarriesHdr(input.outputFormat)) {
4875
4909
  input.log.warn(formatDowngradeWarning(input.outputFormat, forcedHdrWithoutSources));
4876
4910
  effectiveHdr = void 0;
4877
4911
  }
@@ -5685,7 +5719,7 @@ import { gsapCdnDist, normalizeGsapBaseUrl } from "@hyperframes/core/gsap-cdn";
5685
5719
  import {
5686
5720
  assignBundledRuntimeCompositionIds,
5687
5721
  assignMediaRenderIds,
5688
- buildVariablesByCompScript,
5722
+ emitVariablesByCompScript,
5689
5723
  inlineSubCompositions as inlineSubCompositionsShared,
5690
5724
  ensureExternalLinkTag,
5691
5725
  ensureExternalScriptTag,
@@ -7257,9 +7291,9 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
7257
7291
  }
7258
7292
  }
7259
7293
  }
7260
- const variablesByCompScript = buildVariablesByCompScript(result.variablesByComp);
7294
+ emitVariablesByCompScript(document2, result.variablesByComp);
7261
7295
  if (body) {
7262
- let pending = variablesByCompScript ? [variablesByCompScript] : [];
7296
+ let pending = [];
7263
7297
  const flushInline = () => {
7264
7298
  if (!pending.length) return;
7265
7299
  const joined = pending.join("\n;\n");
@@ -9457,7 +9491,7 @@ async function runExtractVideosStage(input) {
9457
9491
  fps: job.config.fps,
9458
9492
  outputDir: join18(compiledDir, "__hyperframes_video_frames"),
9459
9493
  format: job.config.videoFrameFormat ?? "auto",
9460
- toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
9494
+ toneMapHdrToSdr: rendersSdrOnly(job.config),
9461
9495
  timelineEnd: composition.duration,
9462
9496
  maxTransientRetries: extractionPolicy.maxTransientRetries,
9463
9497
  collectProbeFailures: extractionPolicy.failureMode === "enforce",
@@ -14041,7 +14075,12 @@ async function executeRenderPipeline(input) {
14041
14075
  });
14042
14076
  const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
14043
14077
  const presetFormat = outputFormat === "webm" || outputFormat === "mov" ? outputFormat : "mp4";
14044
- const preset = getEncoderPreset3(job.config.quality, presetFormat, encoderHdr);
14078
+ const preset = getEncoderPreset3(
14079
+ job.config.quality,
14080
+ presetFormat,
14081
+ encoderHdr,
14082
+ job.config.proresProfile
14083
+ );
14045
14084
  if (job.config.crf != null && job.config.videoBitrate) {
14046
14085
  log.warn(
14047
14086
  `[Render] Both crf=${job.config.crf} and videoBitrate=${job.config.videoBitrate} were set. These are mutually exclusive; honoring crf and ignoring videoBitrate. Set only one to silence this warning.`
@@ -15071,6 +15110,7 @@ import { parseEncoderOptions } from "@hyperframes/studio-server/encoder-options"
15071
15110
 
15072
15111
  // src/renderRequest.ts
15073
15112
  import {
15113
+ PRORES_PROFILE_NAMES as PRORES_PROFILE_NAMES2,
15074
15114
  isVideoFrameFormat as isVideoFrameFormat2,
15075
15115
  resolveConfig as resolveConfig4,
15076
15116
  validateEngineConfigSnapshot as validateEngineConfigSnapshot2
@@ -15353,6 +15393,7 @@ function assertRequestOptionScalars(options) {
15353
15393
  for (const field of ["entryFile", "videoBitrate", "maxBitrate", "bufferSize"]) {
15354
15394
  assertOptionalString(options, field);
15355
15395
  }
15396
+ assertOptionalEnum(options, "proresProfile", PRORES_PROFILE_NAMES2);
15356
15397
  assertOptionalEnum(options, "strictness", ["strict", "best-effort"]);
15357
15398
  assertOptionalEnum(options, "hdrMode", ["auto", "force-hdr", "force-sdr"]);
15358
15399
  assertOptionalEnum(options, "pageMotionBlur", ["off"]);
@@ -1,4 +1,4 @@
1
- import { type EngineConfig, type VideoFrameFormat } from "@hyperframes/engine";
1
+ import { type EngineConfig, type ProresProfile, type VideoFrameFormat } from "@hyperframes/engine";
2
2
  import { type CanvasResolution, type Fps } from "@hyperframes/core";
3
3
  import type { ProducerLogger } from "./logger.js";
4
4
  import type { RenderConfig } from "./services/renderOrchestrator.js";
@@ -38,6 +38,7 @@ export interface RenderRequestOptions {
38
38
  crf?: number;
39
39
  videoBitrate?: string;
40
40
  keyframeIntervalSeconds?: number;
41
+ proresProfile?: ProresProfile;
41
42
  maxBitrate?: string;
42
43
  bufferSize?: string;
43
44
  videoFrameFormat?: VideoFrameFormat;
@@ -1,5 +1,5 @@
1
1
  import { type CanvasResolution, type DistributedFps } from "@hyperframes/core";
2
- import { type EngineConfig, type VideoFrameFormat, type AudioProcessingFailure } from "@hyperframes/engine";
2
+ import { type EngineConfig, type VideoFrameFormat, type ProresProfile, type AudioProcessingFailure } from "@hyperframes/engine";
3
3
  import { type ProducerLogger } from "../../logger.js";
4
4
  import { type RenderJob, type RenderStrictness } from "../renderOrchestrator.js";
5
5
  import { type ChunkSliceJson, type PlanFileDigests } from "../render/stages/freezePlan.js";
@@ -49,6 +49,8 @@ export interface DistributedRenderConfig extends Pick<RenderRequestOptions, "pag
49
49
  bitrate?: string;
50
50
  /** Longest gap between keyframes, in seconds (mp4). Chunk joins add keyframes, so this is a maximum. */
51
51
  keyframeIntervalSeconds?: number;
52
+ /** ProRes profile for `format: "mov"`; the 422 profiles have no alpha. Defaults to `4444`. */
53
+ proresProfile?: ProresProfile;
52
54
  /** Peak bitrate cap (mp4), per chunk. */
53
55
  maxBitrate?: string;
54
56
  /** Rate-cap buffer; defaults to twice `maxBitrate`. */
@@ -7,6 +7,7 @@ export interface DeliveryEncoderConfig {
7
7
  keyframeIntervalSeconds?: number;
8
8
  maxBitrate?: string;
9
9
  bufferSize?: string;
10
+ proresProfile?: string;
10
11
  }
11
12
  /** The `EncoderOptions` fields the delivery config sets, ready to spread into any encoder input. */
12
13
  export interface DeliveryEncoderOptions {
@@ -34,5 +34,6 @@ export declare function findRenderHdrAutoPromotionTrigger(input: {
34
34
  }[];
35
35
  nativeHdrImageIds: ReadonlySet<string>;
36
36
  }): string | undefined;
37
+ export declare function rendersSdrOnly(config: Pick<RenderConfig, "hdrMode" | "format">): boolean;
37
38
  export declare function resolveEffectiveHdrMode(input: HdrModeInput): EffectiveHdr;
38
39
  export {};
@@ -10,6 +10,7 @@
10
10
  * has in memory.
11
11
  */
12
12
  import type { Fps } from "@hyperframes/core";
13
+ import type { ProresProfile } from "@hyperframes/engine";
13
14
  import type { RenderRequestOptions } from "../../../renderRequest.js";
14
15
  import { type PlanDimensions } from "./planHash.js";
15
16
  /**
@@ -32,6 +33,7 @@ export interface LockedRenderConfig extends Pick<RenderRequestOptions, "pageMoti
32
33
  */
33
34
  quality: "draft" | "standard" | "high";
34
35
  ffmpegVersion: string;
36
+ proresProfile?: ProresProfile;
35
37
  preset: string;
36
38
  crf?: number;
37
39
  bitrate?: string;
@@ -1,5 +1,5 @@
1
1
  import { type CanvasResolution, type Fps, type FpsInput, type HfVfxCapture } from "@hyperframes/core";
2
- import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
2
+ import { type EngineConfig, type ExtractionPhaseBreakdown, type VideoFrameFormat, type CaptureOptions, type CaptureVideoMetadataHint, type CaptureSession, type BeforeCaptureHook, type ProresProfile, type ParallelProgress, type WorkerTask, type CapturePerfSummary, type CaptureWarning, type SubTimelineWaitOutcome, type WorkerSizing, type StaticVerificationOutcome, type CaptureFailureKind, type MotionBlurOptions } from "@hyperframes/engine";
3
3
  import { type ProducerLogger } from "../logger.js";
4
4
  import { type RenderOutputFormat } from "./render/renderFormat.js";
5
5
  import { type RenderCaptureObservability, type RenderObservabilitySummary, type OpenStallAttempt, type StageStallRule } from "./render/observability.js";
@@ -103,6 +103,8 @@ export interface RenderConfig {
103
103
  videoBitrate?: string;
104
104
  /** Keyframe every N seconds (mp4). A maximum where chunks join, as on Lambda. */
105
105
  keyframeIntervalSeconds?: number;
106
+ /** MOV only: ProRes profile. Default `4444` keeps alpha; the 422 profiles drop it. */
107
+ proresProfile?: ProresProfile;
106
108
  /** Peak bitrate cap (e.g. "8M", mp4) alongside CRF. */
107
109
  maxBitrate?: string;
108
110
  /** Rate-cap buffer; defaults to twice `maxBitrate`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/producer",
3
- "version": "0.8.208",
3
+ "version": "0.8.210",
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.2.1",
37
37
  "puppeteer-core": "^25.2.1",
38
- "@hyperframes/core": "0.8.208",
39
- "@hyperframes/lint": "0.8.208",
40
- "@hyperframes/parsers": "0.8.208",
41
- "@hyperframes/studio-server": "0.8.208",
42
- "@hyperframes/engine": "0.8.208"
38
+ "@hyperframes/core": "0.8.210",
39
+ "@hyperframes/engine": "0.8.210",
40
+ "@hyperframes/lint": "0.8.210",
41
+ "@hyperframes/studio-server": "0.8.210",
42
+ "@hyperframes/parsers": "0.8.210"
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.208"
51
+ "@hyperframes/aws-lambda": "0.8.210"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=22"