@hyperframes/studio-server 0.8.204 → 0.8.206

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.
@@ -33,6 +33,12 @@ function parseEncoderOptions(body, format) {
33
33
  const rate = parseRateControl(body, format);
34
34
  if (!rate.ok) return rate;
35
35
  const options = { ...rate.options };
36
+ if (isSet(body.gifLoop)) {
37
+ if (typeof body.gifLoop !== "number" || !Number.isInteger(body.gifLoop) || body.gifLoop < 0 || body.gifLoop > 65535) {
38
+ return { ok: false, error: "gifLoop must be an integer from 0 to 65535" };
39
+ }
40
+ if (format === "gif") options.gifLoop = body.gifLoop;
41
+ }
36
42
  if (isSet(body.videoFrameFormat)) {
37
43
  if (!oneOf(body.videoFrameFormat, VIDEO_FRAME_FORMATS)) {
38
44
  return {
@@ -1,12 +1,14 @@
1
1
  declare const VIDEO_FRAME_FORMATS: readonly ["auto", "jpg", "png"];
2
2
  declare const HDR_MODES: readonly ["auto", "force-hdr", "force-sdr"];
3
3
  interface EncoderOptions {
4
+ gifLoop?: number;
4
5
  crf?: number;
5
6
  videoBitrate?: string;
6
7
  videoFrameFormat?: (typeof VIDEO_FRAME_FORMATS)[number];
7
8
  hdrMode?: (typeof HDR_MODES)[number];
8
9
  }
9
10
  interface EncoderOptionsBody {
11
+ gifLoop?: unknown;
10
12
  crf?: unknown;
11
13
  videoBitrate?: unknown;
12
14
  videoFrameFormat?: unknown;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  parseEncoderOptions
3
- } from "../chunk-GR4TX52A.js";
3
+ } from "../chunk-2GPN2OSU.js";
4
4
  export {
5
5
  parseEncoderOptions
6
6
  };
@@ -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-CdpmyC5i.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-DeICCYb5.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-CdpmyC5i.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-CdpmyC5i.js';
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';
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-GR4TX52A.js";
27
+ } from "./chunk-2GPN2OSU.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(
@@ -4669,12 +4671,13 @@ import { streamSSE } from "hono/streaming";
4669
4671
  import { existsSync as existsSync6, lstatSync as lstatSync3, readFileSync as readFileSync8, unlinkSync as unlinkSync3, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
4670
4672
  import { dirname as dirname5, join as join10 } from "path";
4671
4673
  import { VALID_CANVAS_RESOLUTIONS } from "@hyperframes/parsers";
4672
- import { formatRenderOutputTimestamp, parseFps } from "@hyperframes/core";
4674
+ import { formatRenderOutputTimestamp, parseFps, fpsToNumber } from "@hyperframes/core";
4673
4675
  var VALID_RESOLUTIONS = new Set(VALID_CANVAS_RESOLUTIONS);
4674
4676
  var RENDER_MIME = {
4675
4677
  ".mp4": "video/mp4",
4676
4678
  ".webm": "video/webm",
4677
- ".mov": "video/quicktime"
4679
+ ".mov": "video/quicktime",
4680
+ ".gif": "image/gif"
4678
4681
  };
4679
4682
  var RENDER_EXTENSIONS = Object.keys(RENDER_MIME);
4680
4683
  var RENDER_ARTIFACT_EXTENSIONS = [...RENDER_EXTENSIONS, ".meta.json"];
@@ -4724,11 +4727,17 @@ function registerRenderRoutes(api, adapter) {
4724
4727
  const project = await adapter.resolveProject(c.req.param("id"));
4725
4728
  if (!project) return c.json({ error: "not found" }, 404);
4726
4729
  const body = await c.req.json().catch(() => ({}));
4727
- const VALID_FORMATS = /* @__PURE__ */ new Set(["mp4", "webm", "mov"]);
4728
- const FORMAT_EXT = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
4729
- const format = VALID_FORMATS.has(body.format ?? "") ? body.format : "mp4";
4730
+ const VALID_FORMATS = ["mp4", "webm", "mov", "gif"];
4731
+ const FORMAT_EXT = {
4732
+ mp4: ".mp4",
4733
+ webm: ".webm",
4734
+ mov: ".mov",
4735
+ gif: ".gif"
4736
+ };
4737
+ const format = VALID_FORMATS.find((value) => value === body.format) ?? "mp4";
4730
4738
  const fpsParse = body.fps === void 0 ? null : parseFps(body.fps);
4731
- const fps = fpsParse && fpsParse.ok ? fpsParse.value : { num: 30, den: 1 };
4739
+ const requestedFps = fpsParse && fpsParse.ok ? fpsParse.value : { num: 30, den: 1 };
4740
+ const fps = format === "gif" && fpsToNumber(requestedFps) > 30 ? { num: 30, den: 1 } : requestedFps;
4732
4741
  const quality = ["draft", "standard", "high"].includes(body.quality ?? "") ? body.quality : "standard";
4733
4742
  const outputResolution = VALID_RESOLUTIONS.has(body.resolution ?? "") ? body.resolution : void 0;
4734
4743
  let composition;
@@ -4903,12 +4912,12 @@ function registerRenderRoutes(api, adapter) {
4903
4912
  if (!project) return c.json({ error: "not found" }, 404);
4904
4913
  const rendersDir = adapter.rendersDir(project);
4905
4914
  if (!existsSync6(rendersDir)) return c.json({ renders: [] });
4906
- const files = readdirSync3(rendersDir).filter((f) => f.endsWith(".mp4") || f.endsWith(".webm") || f.endsWith(".mov")).flatMap((f) => {
4915
+ const files = readdirSync3(rendersDir).filter((f) => RENDER_EXTENSIONS.some((extension) => f.endsWith(extension))).flatMap((f) => {
4907
4916
  const fp = resolveWithinProject(rendersDir, f);
4908
4917
  return fp && !isPrivateProjectFile(project.dir, fp) ? [{ f, fp }] : [];
4909
4918
  }).map(({ f, fp }) => {
4910
4919
  const stat2 = statSync5(fp);
4911
- const rid = f.replace(/\.(mp4|webm|mov)$/, "");
4920
+ const rid = f.replace(/\.(mp4|webm|mov|gif)$/, "");
4912
4921
  const metaPath = join10(rendersDir, `${rid}.meta.json`);
4913
4922
  let status = "complete";
4914
4923
  let durationMs;
@@ -154,7 +154,7 @@ interface StudioApiAdapter {
154
154
  startRender(opts: {
155
155
  project: ResolvedProject;
156
156
  outputPath: string;
157
- format: "mp4" | "webm" | "mov";
157
+ format: "mp4" | "webm" | "mov" | "gif";
158
158
  /**
159
159
  * Frame rate as an exact rational. The HTTP layer (POST
160
160
  * `/projects/:id/render`) accepts either a JSON number (integer fps,
@@ -164,6 +164,7 @@ interface StudioApiAdapter {
164
164
  */
165
165
  fps: _hyperframes_core.Fps;
166
166
  quality: string;
167
+ gifLoop?: number;
167
168
  crf?: number;
168
169
  videoBitrate?: string;
169
170
  videoFrameFormat?: "auto" | "jpg" | "png";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.8.204",
3
+ "version": "0.8.206",
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/parsers": "0.8.204",
88
- "@hyperframes/core": "0.8.204"
87
+ "@hyperframes/parsers": "0.8.206",
88
+ "@hyperframes/core": "0.8.206"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/node": "^25.0.10",