@hyperframes/studio-server 0.8.226 → 0.8.227

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.
@@ -25,7 +25,7 @@ import {
25
25
  writeFileSync as writeFileSync2
26
26
  } from "fs";
27
27
  import { availableParallelism, tmpdir, totalmem } from "os";
28
- import { basename as basename3, dirname as dirname3, extname as extname3, isAbsolute, join as join4, relative, resolve, sep } from "path";
28
+ import { basename as basename3, dirname as dirname3, extname as extname4, isAbsolute, join as join4, relative, resolve, sep } from "path";
29
29
  import { hdrToSdrToneMapFilter } from "@hyperframes/core";
30
30
  import { findFfBinary } from "@hyperframes/parsers/ff-binaries";
31
31
  import { alphaDecoderFor } from "@hyperframes/parsers/media-alpha";
@@ -224,17 +224,38 @@ import {
224
224
  closeSync as closeSync2,
225
225
  existsSync as existsSync3,
226
226
  openSync as openSync2,
227
+ readdirSync as readdirSync2,
227
228
  readFileSync as readFileSync2,
228
229
  rmSync,
229
230
  statSync as statSync3,
230
231
  writeSync
231
232
  } from "fs";
232
- import { basename as basename2, dirname as dirname2, join as join3 } from "path";
233
+ import { basename as basename2, dirname as dirname2, extname as extname3, join as join3 } from "path";
233
234
  var POLL_MS = 500;
234
235
  var UNREADABLE_LOCK_MS = 1e4;
235
236
  function lockPathFor(cachePath) {
236
237
  return join3(dirname2(cachePath), `.lock-${basename2(cachePath)}`);
237
238
  }
239
+ var CopyLockStalledError = class extends Error {
240
+ constructor(copy, stallMs) {
241
+ super(
242
+ `another process holds the copy of ${copy} and has made no progress for ${Math.round(stallMs / 1e3)}s`
243
+ );
244
+ this.name = "CopyLockStalledError";
245
+ }
246
+ };
247
+ function lastActivityMs(cachePath, lockPath) {
248
+ const name = basename2(cachePath);
249
+ const stem = basename2(cachePath, extname3(cachePath));
250
+ const mine = (file) => file === basename2(lockPath) || file.startsWith(".tmp-") && (file.endsWith(`-${name}`) || file.includes(`-.part-${stem}-`)) || file.startsWith(`.part-${stem}-`);
251
+ let newest = 0;
252
+ for (const file of readdirSync2(dirname2(cachePath))) {
253
+ if (!mine(file)) continue;
254
+ const changed = statSync3(join3(dirname2(cachePath), file), { throwIfNoEntry: false })?.mtimeMs;
255
+ newest = Math.max(newest, changed ?? 0);
256
+ }
257
+ return newest;
258
+ }
238
259
  function ownerIsAlive(lockPath) {
239
260
  try {
240
261
  const pid = Number(readFileSync2(lockPath, "utf8"));
@@ -264,7 +285,7 @@ function pause(ms, signal) {
264
285
  signal.addEventListener("abort", onAbort, { once: true });
265
286
  });
266
287
  }
267
- async function acquireCopyLock(cachePath, signal) {
288
+ async function acquireCopyLock(cachePath, signal, stallMs) {
268
289
  const lockPath = lockPathFor(cachePath);
269
290
  for (; ; ) {
270
291
  signal.throwIfAborted();
@@ -279,8 +300,10 @@ async function acquireCopyLock(cachePath, signal) {
279
300
  } catch (err) {
280
301
  if (err.code !== "EEXIST") throw err;
281
302
  }
282
- if (ownerIsAlive(lockPath)) await pause(POLL_MS, signal);
283
- else rmSync(lockPath, { force: true });
303
+ if (!ownerIsAlive(lockPath)) rmSync(lockPath, { force: true });
304
+ else if (Date.now() - lastActivityMs(cachePath, lockPath) > stallMs)
305
+ throw new CopyLockStalledError(basename2(cachePath), stallMs);
306
+ else await pause(POLL_MS, signal);
284
307
  }
285
308
  }
286
309
 
@@ -599,7 +622,7 @@ function createPieceGate(size) {
599
622
  }
600
623
  async function encodeInChunks(ranges, chunkPrefix, tempPath, encode, concat, signal, gate, onDone = () => {
601
624
  }) {
602
- const extension = extname3(tempPath);
625
+ const extension = extname4(tempPath);
603
626
  const pieces = ranges.map((_, index) => `${chunkPrefix}${index}${extension}`);
604
627
  const pending = ranges.flatMap((_, index) => existsSync4(pieces[index]) ? [] : [index]);
605
628
  let doneSeconds = 0;
@@ -907,13 +930,19 @@ async function transcodeToCache(job, projectDir, absoluteSourcePath, cachePath,
907
930
  writeCopyIndex(fastIndexPath(cachePath), sameContent);
908
931
  return sameContent;
909
932
  }
910
- const release = await acquireCopyLock(cachePath, job.abandoned.signal);
933
+ const release = await acquireCopyLock(
934
+ cachePath,
935
+ job.abandoned.signal,
936
+ TRANSCODE_STALL_MS
937
+ ).catch((error) => {
938
+ throw error instanceof CopyLockStalledError ? new ProxyTranscodeError(error.message, null, "") : error;
939
+ });
911
940
  if (!release) return cachePath;
912
941
  const tempPath = join4(cacheDir, `.tmp-${randomUUID()}-${basename3(cachePath)}`);
913
942
  try {
914
943
  const chunkPrefix = join4(
915
944
  cacheDir,
916
- `${CHUNK_PREFIX}${basename3(cachePath, extname3(cachePath))}-`
945
+ `${CHUNK_PREFIX}${basename3(cachePath, extname4(cachePath))}-`
917
946
  );
918
947
  await runFfmpeg(
919
948
  absoluteSourcePath,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PROXY_PARAMS_VERSION,
3
3
  resolveProxy
4
- } from "./chunk-KPCW4DQM.js";
4
+ } from "./chunk-G4VPDLXC.js";
5
5
  import {
6
6
  createMediaCodecProbeCache,
7
7
  proxyVariantFor,
@@ -4,8 +4,8 @@ import {
4
4
  isAutoProxyEnabled,
5
5
  proxyEtagSalt,
6
6
  resolvePreviewMediaCodecProbeCache
7
- } from "../chunk-YOCRTHW3.js";
8
- import "../chunk-KPCW4DQM.js";
7
+ } from "../chunk-JVMMZB22.js";
8
+ import "../chunk-G4VPDLXC.js";
9
9
  import "../chunk-FUTDRYQK.js";
10
10
  import "../chunk-X73QTST2.js";
11
11
  import "../chunk-QF7MFJJU.js";
@@ -20,7 +20,7 @@ import {
20
20
  retryProxyCopy,
21
21
  sourceFolderKind,
22
22
  waitForProxy
23
- } from "../chunk-KPCW4DQM.js";
23
+ } from "../chunk-G4VPDLXC.js";
24
24
  import "../chunk-FUTDRYQK.js";
25
25
  import "../chunk-X73QTST2.js";
26
26
  import "../chunk-QF7MFJJU.js";
package/dist/index.js CHANGED
@@ -86,7 +86,7 @@ import {
86
86
  isAutoProxyEnabled,
87
87
  proxyEtagSalt,
88
88
  resolvePreviewMediaCodecProbeCache
89
- } from "./chunk-YOCRTHW3.js";
89
+ } from "./chunk-JVMMZB22.js";
90
90
  import {
91
91
  CACHE_DIR_NAME,
92
92
  PROXY_PENDING_RETRY_AFTER_SECONDS,
@@ -99,7 +99,7 @@ import {
99
99
  resolveProxy,
100
100
  retryProxyCopy,
101
101
  waitForProxy
102
- } from "./chunk-KPCW4DQM.js";
102
+ } from "./chunk-G4VPDLXC.js";
103
103
  import {
104
104
  PROXY_VARIANT_CONFIG,
105
105
  decideMediaProxyEligibility,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperframes/studio-server",
3
- "version": "0.8.226",
3
+ "version": "0.8.227",
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.226",
88
- "@hyperframes/parsers": "0.8.226"
87
+ "@hyperframes/core": "0.8.227",
88
+ "@hyperframes/parsers": "0.8.227"
89
89
  },
90
90
  "devDependencies": {
91
91
  "@types/node": "^25.0.10",