@hyperframes/studio-server 0.8.205 → 0.8.207

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.
@@ -29,10 +29,46 @@ function parseRateControl(body, format) {
29
29
  if (isSet(body.videoBitrate)) return parseVideoBitrate(body.videoBitrate);
30
30
  return { ok: true, options: {} };
31
31
  }
32
+ function parseKeyframeInterval(value) {
33
+ if (typeof value !== "number" || !(value > 0)) {
34
+ return { ok: false, error: "keyframeIntervalSeconds must be a positive number of seconds" };
35
+ }
36
+ return { ok: true, options: { keyframeIntervalSeconds: value } };
37
+ }
38
+ function parseBitrateField(name, value) {
39
+ if (typeof value !== "string" || !VIDEO_BITRATE.test(value)) {
40
+ return { ok: false, error: `${name} must look like "8M", "5000k" or "8000000"` };
41
+ }
42
+ return { ok: true, options: { [name]: value } };
43
+ }
44
+ var DELIVERY_FIELDS = [
45
+ ["keyframeIntervalSeconds", parseKeyframeInterval],
46
+ ["maxBitrate", (value) => parseBitrateField("maxBitrate", value)],
47
+ ["bufferSize", (value) => parseBitrateField("bufferSize", value)]
48
+ ];
49
+ function parseDelivery(body, format) {
50
+ const present = DELIVERY_FIELDS.filter(([name]) => isSet(body[name]));
51
+ if (present.length === 0) return { ok: true, options: {} };
52
+ if (format !== "mp4") {
53
+ return { ok: false, error: "keyframeIntervalSeconds and maxBitrate apply to mp4 renders" };
54
+ }
55
+ if (isSet(body.bufferSize) && !isSet(body.maxBitrate)) {
56
+ return { ok: false, error: "bufferSize requires maxBitrate" };
57
+ }
58
+ const options = {};
59
+ for (const [name, parse] of present) {
60
+ const parsed = parse(body[name]);
61
+ if (!parsed.ok) return parsed;
62
+ Object.assign(options, parsed.options);
63
+ }
64
+ return { ok: true, options };
65
+ }
32
66
  function parseEncoderOptions(body, format) {
33
67
  const rate = parseRateControl(body, format);
34
68
  if (!rate.ok) return rate;
35
- const options = { ...rate.options };
69
+ const delivery = parseDelivery(body, format);
70
+ if (!delivery.ok) return delivery;
71
+ const options = { ...rate.options, ...delivery.options };
36
72
  if (isSet(body.gifLoop)) {
37
73
  if (typeof body.gifLoop !== "number" || !Number.isInteger(body.gifLoop) || body.gifLoop < 0 || body.gifLoop > 65535) {
38
74
  return { ok: false, error: "gifLoop must be an integer from 0 to 65535" };
@@ -56,7 +92,20 @@ function parseEncoderOptions(body, format) {
56
92
  }
57
93
  return { ok: true, options };
58
94
  }
95
+ function encoderJobOptions(opts) {
96
+ const { crf, videoBitrate, keyframeIntervalSeconds, maxBitrate, bufferSize } = opts;
97
+ return {
98
+ ...crf !== void 0 ? { crf } : {},
99
+ ...videoBitrate ? { videoBitrate } : {},
100
+ ...keyframeIntervalSeconds !== void 0 ? { keyframeIntervalSeconds } : {},
101
+ ...maxBitrate ? { maxBitrate } : {},
102
+ ...bufferSize ? { bufferSize } : {},
103
+ ...opts.videoFrameFormat ? { videoFrameFormat: opts.videoFrameFormat } : {},
104
+ ...opts.hdrMode ? { hdrMode: opts.hdrMode } : {}
105
+ };
106
+ }
59
107
 
60
108
  export {
61
- parseEncoderOptions
109
+ parseEncoderOptions,
110
+ encoderJobOptions
62
111
  };
@@ -6,6 +6,9 @@ interface EncoderOptions {
6
6
  videoBitrate?: string;
7
7
  videoFrameFormat?: (typeof VIDEO_FRAME_FORMATS)[number];
8
8
  hdrMode?: (typeof HDR_MODES)[number];
9
+ keyframeIntervalSeconds?: number;
10
+ maxBitrate?: string;
11
+ bufferSize?: string;
9
12
  }
10
13
  interface EncoderOptionsBody {
11
14
  gifLoop?: unknown;
@@ -13,6 +16,9 @@ interface EncoderOptionsBody {
13
16
  videoBitrate?: unknown;
14
17
  videoFrameFormat?: unknown;
15
18
  hdrMode?: unknown;
19
+ keyframeIntervalSeconds?: unknown;
20
+ maxBitrate?: unknown;
21
+ bufferSize?: unknown;
16
22
  }
17
23
  type Parsed = {
18
24
  ok: true;
@@ -23,5 +29,6 @@ type Parsed = {
23
29
  };
24
30
  /** Explicit encoder overrides from a render request. MOV (ProRes) takes no rate control. */
25
31
  declare function parseEncoderOptions(body: EncoderOptionsBody, format: string): Parsed;
32
+ declare function encoderJobOptions(opts: EncoderOptions): Omit<EncoderOptions, "gifLoop">;
26
33
 
27
- export { type EncoderOptionsBody, parseEncoderOptions };
34
+ export { type EncoderOptionsBody, encoderJobOptions, parseEncoderOptions };
@@ -1,6 +1,8 @@
1
1
  import {
2
+ encoderJobOptions,
2
3
  parseEncoderOptions
3
- } from "../chunk-2GPN2OSU.js";
4
+ } from "../chunk-ZR3T4TKU.js";
4
5
  export {
6
+ encoderJobOptions,
5
7
  parseEncoderOptions
6
8
  };
@@ -1,5 +1,5 @@
1
1
  import '@hyperframes/core';
2
- export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-DeICCYb5.js';
2
+ export { a as PreviewApiAdapter, P as PreviewDocumentStore, i as injectMediaCodecMap, f as injectMediaCodecMapIntoHtml, g as isAutoProxyEnabled, p as proxyEtagSalt, r as resolvePreviewMediaCodecProbeCache } from '../mediaProxyPreview-DIEtpLJo.js';
3
3
  import './mediaCodecMap.js';
4
4
  import 'sharp';
5
5
  import '@hyperframes/parsers';
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
- import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-DeICCYb5.js';
3
- export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-DeICCYb5.js';
2
+ import { S as StudioApiAdapter, P as PreviewDocumentStore, M as MediaProcessingJobState } from './mediaProxyPreview-DIEtpLJo.js';
3
+ export { L as LintResult, a as PreviewApiAdapter, R as RenderJobState, b as ResolvedProject, c as StudioSelectionResponse, d as StudioSelectionSnapshot, e as StudioSelectionTextField } from './mediaProxyPreview-DIEtpLJo.js';
4
4
  export { C as ClosedWindow, D as DEFAULT_HISTORY_ROOT, S as HISTORY_START, H as HistoryClosedError, a as HistoryEntry, b as HistoryEntrySide, c as HistoryFileChange, d as HistoryListItem, e as HistoryRefusedError, f as HistoryResult, g as HistoryTarget, h as HistoryWho, i as HistoryWindow, M as MAX_WINDOW_IDLE_MS, P as ProjectHistory, j as ProjectHistoryOptions, k as ProjectHistoryView, U as UNDO_MODES, l as UndoMode, o as openProjectHistory, r as readProjectHistory } from './projectHistory-C18HDvac.js';
5
5
  export { ArtifactCapture, ArtifactInput, ArtifactRecord, HistoryBusyError, HistoryIdError, ProjectHistoryRecord, PrunedHistory, ReclaimedHistory, RenderRecord, VideoState, artifactDirectory, beginArtifactCapture, finishSnapshotRun, forgetRecordedArtifacts, historyCache, listProjectHistories, pruneGoneProjectHistories, reclaimUncopiedBlobs, recordArtifact, recordArtifacts, recordRenderSuccess, registerRenderOutput, retainSnapshotOutput } from './history/index.js';
6
6
  export { CACHE_DIR_NAME as PROXY_CACHE_DIR_NAME } from './helpers/proxyTranscoder.js';
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  } from "./chunk-FJBJJURY.js";
25
25
  import {
26
26
  parseEncoderOptions
27
- } from "./chunk-2GPN2OSU.js";
27
+ } from "./chunk-ZR3T4TKU.js";
28
28
  import {
29
29
  DEFAULT_HISTORY_ROOT,
30
30
  DELETED_VERSION,
@@ -3871,6 +3871,7 @@ function registerFileRoutes(api, adapter) {
3871
3871
  // src/routes/preview.ts
3872
3872
  import { parseHTML as parseHTML7 } from "linkedom";
3873
3873
  import { createReadStream, existsSync as existsSync5, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
3874
+ import { stripSdrPngColourTags } from "@hyperframes/core/compiler/png-colour-tags";
3874
3875
  import { Readable } from "stream";
3875
3876
  import { join as join8, resolve as resolve4 } from "path";
3876
3877
  import { createHash as createHash2 } from "crypto";
@@ -4567,8 +4568,9 @@ ${etag}`;
4567
4568
  servedContentType = PROXY_VARIANT_CONFIG[proxyVariant].contentType;
4568
4569
  }
4569
4570
  const textBuffer = isText ? Buffer.from(readFileSync6(file, "utf-8"), "utf-8") : null;
4570
- const totalSize = textBuffer ? textBuffer.length : statSync4(servedPath).size;
4571
- const bodyFor = (start, end) => textBuffer ? new Uint8Array(textBuffer.subarray(start, end + 1)) : (
4571
+ const heldBuffer = textBuffer ?? (contentType === "image/png" ? stripSdrPngColourTags(readFileSync6(file)) : null);
4572
+ const totalSize = heldBuffer ? heldBuffer.length : statSync4(servedPath).size;
4573
+ const bodyFor = (start, end) => heldBuffer ? new Uint8Array(heldBuffer.subarray(start, end + 1)) : (
4572
4574
  // Node's web stream type and the DOM one do not overlap for tsc on
4573
4575
  // every platform's lib set; the double cast is the documented bridge.
4574
4576
  Readable.toWeb(
@@ -6670,9 +6672,12 @@ function registerHistoryRoutes(api, adapter) {
6670
6672
  }
6671
6673
 
6672
6674
  // src/helpers/previewErrorPage.ts
6673
- import { STUDIO_PREVIEW_ERRORS as STUDIO_PREVIEW_ERRORS2 } from "@hyperframes/core/studio-preview-mark";
6675
+ import {
6676
+ refusedPreviewMessage,
6677
+ STUDIO_PREVIEW_ERRORS as STUDIO_PREVIEW_ERRORS2
6678
+ } from "@hyperframes/core/studio-preview-mark";
6674
6679
  function previewErrorPage(status) {
6675
- const message = `The preview page could not be loaded (HTTP ${status}).`;
6680
+ const message = refusedPreviewMessage(status);
6676
6681
  return `<!doctype html><meta charset="utf-8"><script>window.${STUDIO_PREVIEW_ERRORS2}=[${JSON.stringify(message)}]</script><p>${message}</p>`;
6677
6682
  }
6678
6683
 
@@ -167,6 +167,9 @@ interface StudioApiAdapter {
167
167
  gifLoop?: number;
168
168
  crf?: number;
169
169
  videoBitrate?: string;
170
+ keyframeIntervalSeconds?: number;
171
+ maxBitrate?: string;
172
+ bufferSize?: string;
170
173
  videoFrameFormat?: "auto" | "jpg" | "png";
171
174
  hdrMode?: "auto" | "force-hdr" | "force-sdr";
172
175
  jobId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.8.205",
3
+ "version": "0.8.207",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -84,8 +84,8 @@
84
84
  "linkedom": "^0.18.12",
85
85
  "postcss": "^8.5.8",
86
86
  "postcss-selector-parser": "^7.1.2",
87
- "@hyperframes/core": "0.8.205",
88
- "@hyperframes/parsers": "0.8.205"
87
+ "@hyperframes/core": "0.8.207",
88
+ "@hyperframes/parsers": "0.8.207"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/node": "^25.0.10",