@hyperframes/studio-server 0.9.12 → 0.9.14

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.
@@ -404,8 +404,13 @@ function canonicalizeProxySource(projectDir, absoluteSourcePath) {
404
404
  }
405
405
  return { projectDir: realProjectRoot(projectDir), sourcePath: realpath(absoluteSourcePath) };
406
406
  }
407
- function getCanonicalProxyCachePath(source, variant, box) {
408
- const key = fastCopyKey(source.sourcePath, PROXY_PARAMS_VERSION, variant, box);
407
+ function getCanonicalProxyCachePath(source, variant, box, limitSeconds) {
408
+ const key = fastCopyKey(
409
+ source.sourcePath,
410
+ PROXY_PARAMS_VERSION,
411
+ keyVariant(variant, limitSeconds),
412
+ box
413
+ );
409
414
  const made = join4(
410
415
  source.projectDir,
411
416
  CACHE_DIR_NAME,
@@ -413,13 +418,28 @@ function getCanonicalProxyCachePath(source, variant, box) {
413
418
  );
414
419
  return existsSync4(made) ? made : readCopyIndex(fastIndexPath(made)) ?? made;
415
420
  }
416
- function getProxyCachePath(projectDir, absoluteSourcePath, variant = "h264", box) {
421
+ function getProxyCachePath(projectDir, absoluteSourcePath, variant = "h264", box, limitSeconds) {
417
422
  return getCanonicalProxyCachePath(
418
423
  canonicalizeProxySource(projectDir, absoluteSourcePath),
419
424
  variant,
420
- box
425
+ box,
426
+ limitSeconds
421
427
  );
422
428
  }
429
+ function copiedSeconds(duration, limitSeconds) {
430
+ return limitSeconds === void 0 ? duration : Math.min(limitSeconds, duration ?? limitSeconds);
431
+ }
432
+ function lengthArgs(seconds) {
433
+ return seconds === void 0 ? [] : ["-t", String(seconds)];
434
+ }
435
+ function keyVariant(variant, limitSeconds) {
436
+ return limitSeconds === void 0 ? variant : `${variant}@${limitSeconds}s`;
437
+ }
438
+ async function proxyWindowSeconds(absoluteSourcePath, neededSeconds) {
439
+ const { durationSeconds } = await probeMediaMetadata(absoluteSourcePath);
440
+ const window = Math.ceil((neededSeconds + 1) / PROXY_CHUNK_SECONDS) * PROXY_CHUNK_SECONDS;
441
+ return durationSeconds && window + PROXY_CHUNK_SECONDS <= durationSeconds ? window : void 0;
442
+ }
423
443
  function reportProgress(job, progress) {
424
444
  job.lastProgress = progress;
425
445
  for (const listener of job.progress) listener(progress);
@@ -757,7 +777,7 @@ async function buildVideoFilter(ffmpegPath, sourcePath, metadata, keepsAlpha, bo
757
777
  `format=${pixelFormat}`
758
778
  ].join(",") : [evenScale, `format=${pixelFormat}`].join(",");
759
779
  }
760
- async function runFfmpeg(sourcePath, outputPath, chunkPrefix, variant, box, signal, onProgress, isBackground = () => false) {
780
+ async function runFfmpeg(sourcePath, outputPath, chunkPrefix, variant, box, limitSeconds, signal, onProgress, isBackground = () => false) {
761
781
  const metadata = await probeMediaMetadata(sourcePath);
762
782
  const ffmpegPath = findFfBinary("ffmpeg", { configuredMustExist: true });
763
783
  if (!ffmpegPath) {
@@ -771,7 +791,7 @@ async function runFfmpeg(sourcePath, outputPath, chunkPrefix, variant, box, sign
771
791
  ...["-map", "0:V:0"],
772
792
  ...metadata.audioStream === void 0 ? [] : ["-map", `0:a:${metadata.audioStream}`]
773
793
  ];
774
- const totalSeconds = metadata.durationSeconds;
794
+ const totalSeconds = copiedSeconds(metadata.durationSeconds, limitSeconds);
775
795
  const gate = keepsAlpha ? ALPHA_PIECE_GATE : UNGATED;
776
796
  const vp8Threads = (pieces) => [
777
797
  "-threads",
@@ -789,7 +809,7 @@ async function runFfmpeg(sourcePath, outputPath, chunkPrefix, variant, box, sign
789
809
  "-i",
790
810
  sourcePath,
791
811
  ...streamArgs,
792
- ...range ? ["-t", String(range.lengthSeconds)] : [],
812
+ ...lengthArgs(range?.lengthSeconds ?? limitSeconds),
793
813
  "-vf",
794
814
  videoFilter,
795
815
  ...KEYFRAME_EVERY_SECOND_ARGS,
@@ -921,7 +941,7 @@ function spawnFfmpeg(ffmpegPath, args, stallMs, signal, onProgress, background =
921
941
  });
922
942
  });
923
943
  }
924
- async function transcodeToCache(job, projectDir, absoluteSourcePath, cachePath, variant, box) {
944
+ async function transcodeToCache(job, projectDir, absoluteSourcePath, cachePath, variant, box, limitSeconds) {
925
945
  await acquireSlot(job);
926
946
  try {
927
947
  if (existsSync4(cachePath)) return cachePath;
@@ -932,7 +952,7 @@ async function transcodeToCache(job, projectDir, absoluteSourcePath, cachePath,
932
952
  await contentCopyKey(
933
953
  absoluteSourcePath,
934
954
  PROXY_PARAMS_VERSION,
935
- variant,
955
+ keyVariant(variant, limitSeconds),
936
956
  box,
937
957
  job.abandoned.signal
938
958
  )
@@ -962,6 +982,7 @@ async function transcodeToCache(job, projectDir, absoluteSourcePath, cachePath,
962
982
  chunkPrefix,
963
983
  variant,
964
984
  box,
985
+ limitSeconds,
965
986
  job.abandoned.signal,
966
987
  (progress) => reportProgress(job, progress),
967
988
  () => job.background && !job.priority
@@ -997,7 +1018,7 @@ function proxyActivityMark(projectDir) {
997
1018
  async function resolveProxy(projectDir, absoluteSourcePath, variant = "h264", box, options = {}) {
998
1019
  options.signal?.throwIfAborted();
999
1020
  const source = canonicalizeProxySource(projectDir, absoluteSourcePath);
1000
- const cachePath = getCanonicalProxyCachePath(source, variant, box);
1021
+ const cachePath = getCanonicalProxyCachePath(source, variant, box, options.limitSeconds);
1001
1022
  if (existsSync4(cachePath)) {
1002
1023
  markCacheEntryUsed(cachePath);
1003
1024
  maintainProxyCache(dirname3(cachePath));
@@ -1028,7 +1049,8 @@ async function resolveProxy(projectDir, absoluteSourcePath, variant = "h264", bo
1028
1049
  source.sourcePath,
1029
1050
  cachePath,
1030
1051
  variant,
1031
- box
1052
+ box,
1053
+ options.limitSeconds
1032
1054
  ).catch((err) => {
1033
1055
  if (!(err instanceof ProxyDroppedError) && !job.abandoned.signal.aborted) {
1034
1056
  rememberFailure(cachePath, err);
@@ -1069,8 +1091,9 @@ async function resolveProxies(projectDir, sources, onSettled = () => {
1069
1091
  let next = 0;
1070
1092
  const worker = async () => {
1071
1093
  for (let index = next++; index < sources.length; index = next++) {
1072
- const { sourcePath, variant, box } = sources[index];
1094
+ const { sourcePath, variant, box, limitSeconds } = sources[index];
1073
1095
  results[index] = await resolveProxy(projectDir, sourcePath, variant, box, {
1096
+ limitSeconds,
1074
1097
  onProgress: (progress) => onProgress(index, progress),
1075
1098
  signal
1076
1099
  }).then(
@@ -1150,6 +1173,7 @@ export {
1150
1173
  ProxyWaitTimeoutError,
1151
1174
  waitForProxy,
1152
1175
  getProxyCachePath,
1176
+ proxyWindowSeconds,
1153
1177
  clearFailedTranscodesForTest,
1154
1178
  sourceFolderKind,
1155
1179
  proxyCopyProgress,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PROXY_PARAMS_VERSION,
3
3
  resolveProxy
4
- } from "./chunk-BAJ6HKBU.js";
4
+ } from "./chunk-B356RYG5.js";
5
5
  import {
6
6
  createMediaCodecProbeCache,
7
7
  proxyVariantFor,
@@ -347,9 +347,10 @@ function resolveElementTiming(el) {
347
347
  const timing = readClipTiming(el);
348
348
  return { start: timing.start ?? 0, duration: timing.duration ?? 0 };
349
349
  }
350
+ var roundMs = (seconds) => Math.round(seconds * 1e3) / 1e3;
350
351
  function setElementDuration(el, start, duration, trackIndex) {
351
352
  writeClipTiming(el, {
352
- start: Math.round(start * 1e3) / 1e3,
353
+ start: typeof start === "number" ? Math.round(start * 1e3) / 1e3 : start,
353
354
  duration: Math.round(duration * 1e3) / 1e3,
354
355
  ...trackIndex != null ? { trackIndex } : {}
355
356
  });
@@ -369,6 +370,12 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
369
370
  const timed = timedMedia ?? el;
370
371
  const timing = resolveElementTiming(timed);
371
372
  let { start, duration } = timing;
373
+ const reference = readClipTiming(timed).startExpression;
374
+ const startsAfter = reference?.kind === "reference" ? reference : null;
375
+ if (startsAfter) {
376
+ if (!fallbackTiming) return { html: source, matched: false, newId: null };
377
+ start = fallbackTiming.start;
378
+ }
372
379
  if (duration <= 0 && fallbackTiming && fallbackTiming.duration > 0) {
373
380
  start = fallbackTiming.start;
374
381
  duration = fallbackTiming.duration;
@@ -391,7 +398,12 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
391
398
  duplicateCssRulesForId(document, timedMedia.id, mediaId);
392
399
  clonedTimed.setAttribute("id", mediaId);
393
400
  }
394
- setElementDuration(clonedTimed, splitTime, secondDuration, fallbackTiming?.track);
401
+ setElementDuration(
402
+ clonedTimed,
403
+ startsAfter ? { ...startsAfter, offset: roundMs(startsAfter.offset + firstDuration) } : splitTime,
404
+ secondDuration,
405
+ fallbackTiming?.track
406
+ );
395
407
  const playbackStartAttr = timed.hasAttribute("data-playback-start") ? "data-playback-start" : timed.hasAttribute("data-media-start") ? "data-media-start" : timed.matches("[data-composition-src], [data-composition-id]") ? "data-playback-start" : timed.matches("audio, video") ? "data-media-start" : null;
396
408
  if (playbackStartAttr) {
397
409
  const readAttr = (name) => timed.getAttribute(name);
@@ -409,7 +421,7 @@ function splitElementInHtml(source, target, splitTime, newId, fallbackTiming) {
409
421
  if (originalId) {
410
422
  duplicateCssRulesForId(document, originalId, newId);
411
423
  }
412
- setElementDuration(timed, start, firstDuration, fallbackTiming?.track);
424
+ setElementDuration(timed, startsAfter ?? start, firstDuration, fallbackTiming?.track);
413
425
  if (el.nextSibling) {
414
426
  el.parentElement.insertBefore(clone, el.nextSibling);
415
427
  } else {
@@ -4,8 +4,8 @@ import {
4
4
  isAutoProxyEnabled,
5
5
  proxyEtagSalt,
6
6
  resolvePreviewMediaCodecProbeCache
7
- } from "../chunk-J4SRRKQU.js";
8
- import "../chunk-BAJ6HKBU.js";
7
+ } from "../chunk-GTQ6S4O5.js";
8
+ import "../chunk-B356RYG5.js";
9
9
  import "../chunk-NOIHVBIQ.js";
10
10
  import "../chunk-X73QTST2.js";
11
11
  import "../chunk-QF7MFJJU.js";
@@ -50,7 +50,8 @@ declare function waitForProxy<T>(promise: Promise<T>, timeoutMs?: number): Promi
50
50
  * transcoding anything, so a host can check cache state. Pass the request's
51
51
  * preview box (`hf-proxy-box`) to name a boxed copy.
52
52
  */
53
- declare function getProxyCachePath(projectDir: string, absoluteSourcePath: string, variant?: ProxyVariant, box?: PreviewProxyBox): string;
53
+ declare function getProxyCachePath(projectDir: string, absoluteSourcePath: string, variant?: ProxyVariant, box?: PreviewProxyBox, limitSeconds?: number): string;
54
+ declare function proxyWindowSeconds(absoluteSourcePath: string, neededSeconds: number): Promise<number | undefined>;
54
55
  /** How far a copy has got: seconds of the source done, out of its length when that is known. */
55
56
  interface ProxyProgress {
56
57
  doneSeconds: number;
@@ -86,10 +87,12 @@ declare function resolveProxies(projectDir: string, sources: ReadonlyArray<{
86
87
  sourcePath: string;
87
88
  variant: ProxyVariant;
88
89
  box?: PreviewProxyBox;
90
+ limitSeconds?: number;
89
91
  }>, onSettled?: (index: number) => void, onProgress?: (index: number, progress: ProxyProgress) => void, signal?: AbortSignal): Promise<PromiseSettledResult<string>[]>;
90
92
  /** One JPEG of the source at a time (to a tenth of a second), sized like the copy's box. */
91
93
  declare function resolveStill(projectDir: string, absoluteSourcePath: string, atSeconds: number, box?: PreviewProxyBox): Promise<string>;
92
94
  interface ResolveProxyOptions {
95
+ limitSeconds?: number;
93
96
  priority?: boolean;
94
97
  background?: boolean;
95
98
  signal?: AbortSignal;
@@ -97,4 +100,4 @@ interface ResolveProxyOptions {
97
100
  onProgress?: (progress: ProxyProgress) => void;
98
101
  }
99
102
 
100
- export { CACHE_DIR_NAME, DEFAULT_PROXY_WAIT_TIMEOUT_MS, FfmpegMissingFilterError, FfmpegUnavailableError, PROXY_CHUNK_SECONDS, PROXY_PARAMS_VERSION, PROXY_PENDING_RETRY_AFTER_SECONDS, ProxyCapacityError, type ProxyProgress, ProxySourceOutsideProjectError, ProxyTranscodeError, ProxyWaitTimeoutError, type ResolveProxyOptions, type SourceFolderKind, TRANSCODE_STALL_MS, clearFailedTranscodesForTest, getProxyCachePath, proxyActivityMark, proxyCopyProgress, resolveProxies, resolveProxy, resolveStill, retryProxyCopy, sourceFolderKind, waitForProxy };
103
+ export { CACHE_DIR_NAME, DEFAULT_PROXY_WAIT_TIMEOUT_MS, FfmpegMissingFilterError, FfmpegUnavailableError, PROXY_CHUNK_SECONDS, PROXY_PARAMS_VERSION, PROXY_PENDING_RETRY_AFTER_SECONDS, ProxyCapacityError, type ProxyProgress, ProxySourceOutsideProjectError, ProxyTranscodeError, ProxyWaitTimeoutError, type ResolveProxyOptions, type SourceFolderKind, TRANSCODE_STALL_MS, clearFailedTranscodesForTest, getProxyCachePath, proxyActivityMark, proxyCopyProgress, proxyWindowSeconds, resolveProxies, resolveProxy, resolveStill, retryProxyCopy, sourceFolderKind, waitForProxy };
@@ -15,13 +15,14 @@ import {
15
15
  getProxyCachePath,
16
16
  proxyActivityMark,
17
17
  proxyCopyProgress,
18
+ proxyWindowSeconds,
18
19
  resolveProxies,
19
20
  resolveProxy,
20
21
  resolveStill,
21
22
  retryProxyCopy,
22
23
  sourceFolderKind,
23
24
  waitForProxy
24
- } from "../chunk-BAJ6HKBU.js";
25
+ } from "../chunk-B356RYG5.js";
25
26
  import "../chunk-NOIHVBIQ.js";
26
27
  import "../chunk-X73QTST2.js";
27
28
  import "../chunk-QF7MFJJU.js";
@@ -44,6 +45,7 @@ export {
44
45
  getProxyCachePath,
45
46
  proxyActivityMark,
46
47
  proxyCopyProgress,
48
+ proxyWindowSeconds,
47
49
  resolveProxies,
48
50
  resolveProxy,
49
51
  resolveStill,
@@ -13,7 +13,7 @@ import {
13
13
  splitElementInHtml,
14
14
  unwrapElementsFromHtml,
15
15
  wrapElementsInHtml
16
- } from "../chunk-LRTNNDXS.js";
16
+ } from "../chunk-LK3ZTZGN.js";
17
17
  export {
18
18
  dedupeClonedCompositionId,
19
19
  findTargetElement,
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  splitElementInHtml,
18
18
  unwrapElementsFromHtml,
19
19
  wrapElementsInHtml
20
- } from "./chunk-LRTNNDXS.js";
20
+ } from "./chunk-LK3ZTZGN.js";
21
21
  import {
22
22
  isCompositionSource,
23
23
  stampProjectHfIds,
@@ -91,7 +91,7 @@ import {
91
91
  isAutoProxyEnabled,
92
92
  proxyEtagSalt,
93
93
  resolvePreviewMediaCodecProbeCache
94
- } from "./chunk-J4SRRKQU.js";
94
+ } from "./chunk-GTQ6S4O5.js";
95
95
  import {
96
96
  CACHE_DIR_NAME,
97
97
  PROXY_PENDING_RETRY_AFTER_SECONDS,
@@ -105,7 +105,7 @@ import {
105
105
  resolveStill,
106
106
  retryProxyCopy,
107
107
  waitForProxy
108
- } from "./chunk-BAJ6HKBU.js";
108
+ } from "./chunk-B356RYG5.js";
109
109
  import {
110
110
  PROXY_VARIANT_CONFIG,
111
111
  decideMediaProxyEligibility,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.9.12",
3
+ "version": "0.9.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/heygen-com/hyperframes",
@@ -88,8 +88,8 @@
88
88
  "linkedom": "^0.18.12",
89
89
  "postcss": "^8.5.8",
90
90
  "postcss-selector-parser": "^7.1.2",
91
- "@hyperframes/core": "0.9.12",
92
- "@hyperframes/parsers": "0.9.12"
91
+ "@hyperframes/core": "0.9.14",
92
+ "@hyperframes/parsers": "0.9.14"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@types/node": "^25.0.10",