@hyperframes/producer 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.
- package/dist/distributed.js +762 -442
- package/dist/hyperframe.manifest.json +1 -1
- package/dist/hyperframe.runtime.iife.js +66 -64
- package/dist/index.d.ts +2 -0
- package/dist/index.js +835 -511
- package/dist/public-server.js +557 -235
- package/dist/services/animatedGifPrep.d.ts +6 -0
- package/dist/services/assetMediaType.d.ts +1 -0
- package/dist/services/heifImagePrep.d.ts +69 -0
- package/dist/services/heifRotation.d.ts +2 -0
- package/dist/services/htmlCompiler.d.ts +1 -4
- package/package.json +7 -7
package/dist/public-server.js
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
// src/server.ts
|
|
4
4
|
import {
|
|
5
5
|
existsSync as existsSync19,
|
|
6
|
-
mkdirSync as
|
|
7
|
-
statSync as
|
|
6
|
+
mkdirSync as mkdirSync11,
|
|
7
|
+
statSync as statSync10,
|
|
8
8
|
mkdtempSync as mkdtempSync4,
|
|
9
|
-
writeFileSync as
|
|
10
|
-
rmSync as
|
|
9
|
+
writeFileSync as writeFileSync7,
|
|
10
|
+
rmSync as rmSync13,
|
|
11
11
|
createReadStream as createReadStream3
|
|
12
12
|
} from "node:fs";
|
|
13
|
-
import { resolve as
|
|
13
|
+
import { resolve as resolve10, dirname as dirname13, join as join31 } from "node:path";
|
|
14
14
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
15
15
|
import { parseArgs } from "node:util";
|
|
16
16
|
import crypto from "node:crypto";
|
|
@@ -23,13 +23,13 @@ import { fetchPublicHttpsText as fetchPublicHttpsText2 } from "@hyperframes/engi
|
|
|
23
23
|
import { audioFailureDiagnostics as audioFailureDiagnostics2 } from "@hyperframes/engine";
|
|
24
24
|
import {
|
|
25
25
|
existsSync as existsSync16,
|
|
26
|
-
mkdirSync as
|
|
27
|
-
readFileSync as
|
|
26
|
+
mkdirSync as mkdirSync10,
|
|
27
|
+
readFileSync as readFileSync9,
|
|
28
28
|
readdirSync as readdirSync5,
|
|
29
|
-
rmSync as
|
|
30
|
-
statSync as
|
|
31
|
-
writeFileSync as
|
|
32
|
-
copyFileSync as
|
|
29
|
+
rmSync as rmSync12,
|
|
30
|
+
statSync as statSync8,
|
|
31
|
+
writeFileSync as writeFileSync6,
|
|
32
|
+
copyFileSync as copyFileSync5,
|
|
33
33
|
appendFileSync
|
|
34
34
|
} from "fs";
|
|
35
35
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
@@ -77,7 +77,7 @@ import {
|
|
|
77
77
|
augmentProtocolTimeoutError,
|
|
78
78
|
augmentPageNavigationTimeoutError
|
|
79
79
|
} from "@hyperframes/engine";
|
|
80
|
-
import { join as
|
|
80
|
+
import { join as join27, dirname as dirname11, resolve as resolve8 } from "path";
|
|
81
81
|
import { totalmem as totalmem2 } from "node:os";
|
|
82
82
|
import { randomUUID } from "crypto";
|
|
83
83
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -101,6 +101,7 @@ import { Readable } from "node:stream";
|
|
|
101
101
|
import { join, extname, resolve as resolve2, sep } from "node:path";
|
|
102
102
|
import { injectScriptsAtHeadStart, injectScriptsIntoHtml } from "@hyperframes/core/compiler";
|
|
103
103
|
import { fpsToNumber } from "@hyperframes/core";
|
|
104
|
+
import { stripSdrPngColourTags } from "@hyperframes/core/compiler/png-colour-tags";
|
|
104
105
|
|
|
105
106
|
// src/services/hyperframeRuntimeLoader.ts
|
|
106
107
|
import { createHash } from "node:crypto";
|
|
@@ -217,11 +218,11 @@ var defaultLogger = createConsoleLogger("info");
|
|
|
217
218
|
|
|
218
219
|
// src/services/fileServer.ts
|
|
219
220
|
var readFileAsync = promisify(readFile);
|
|
220
|
-
function isPathInside(
|
|
221
|
+
function isPathInside(child2, parent, options = {}) {
|
|
221
222
|
const { resolveSymlinks = false, pathModule } = options;
|
|
222
223
|
const resolveFn = pathModule?.resolve ?? resolve2;
|
|
223
224
|
const separator = pathModule?.sep ?? sep;
|
|
224
|
-
const resolvedChild = resolveFn(
|
|
225
|
+
const resolvedChild = resolveFn(child2);
|
|
225
226
|
const resolvedParent = resolveFn(parent);
|
|
226
227
|
const normalizedChild = resolveSymlinks && existsSync2(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
|
|
227
228
|
const normalizedParent = resolveSymlinks && existsSync2(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
|
|
@@ -741,6 +742,18 @@ function createFileServer(options) {
|
|
|
741
742
|
html = isIndex ? injectScriptsIntoHtml(html, headScripts, bodyScripts, stripEmbeddedRuntime) : html;
|
|
742
743
|
return c.text(html, 200, { "Content-Type": contentType });
|
|
743
744
|
}
|
|
745
|
+
if (ext === ".png") {
|
|
746
|
+
const untagged = stripSdrPngColourTags(await readFileAsync(fd));
|
|
747
|
+
if (untagged) {
|
|
748
|
+
const headers = {
|
|
749
|
+
"Content-Type": contentType,
|
|
750
|
+
"Content-Length": String(untagged.length)
|
|
751
|
+
};
|
|
752
|
+
return new Response(c.req.method === "HEAD" ? null : new Uint8Array(untagged), {
|
|
753
|
+
headers
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
}
|
|
744
757
|
const totalSize = stat.size;
|
|
745
758
|
const rangeHeader = c.req.header("range");
|
|
746
759
|
const rangeRequest = parseRangeHeader(rangeHeader, totalSize);
|
|
@@ -768,7 +781,8 @@ function createFileServer(options) {
|
|
|
768
781
|
const stream = createReadStream(filePath, {
|
|
769
782
|
fd,
|
|
770
783
|
autoClose: true,
|
|
771
|
-
|
|
784
|
+
start: range?.start ?? 0,
|
|
785
|
+
...range ? { end: range.end } : {}
|
|
772
786
|
});
|
|
773
787
|
streamOwnsFd = true;
|
|
774
788
|
try {
|
|
@@ -782,10 +796,10 @@ function createFileServer(options) {
|
|
|
782
796
|
if (!streamOwnsFd) closeSync(fd);
|
|
783
797
|
}
|
|
784
798
|
});
|
|
785
|
-
return new Promise((
|
|
799
|
+
return new Promise((resolve11) => {
|
|
786
800
|
const connections = /* @__PURE__ */ new Set();
|
|
787
801
|
const server = serve({ fetch: app.fetch, port, hostname: "127.0.0.1" }, (info) => {
|
|
788
|
-
|
|
802
|
+
resolve11({
|
|
789
803
|
url: `http://localhost:${info.port}`,
|
|
790
804
|
port: info.port,
|
|
791
805
|
addPreHeadScript: (script) => {
|
|
@@ -1292,8 +1306,8 @@ var OrderedRenderEventPublisher = class {
|
|
|
1292
1306
|
let timer;
|
|
1293
1307
|
const drained = await Promise.race([
|
|
1294
1308
|
this.tail.then(() => true),
|
|
1295
|
-
new Promise((
|
|
1296
|
-
timer = setTimeout(() =>
|
|
1309
|
+
new Promise((resolve11) => {
|
|
1310
|
+
timer = setTimeout(() => resolve11(false), FLUSH_TIMEOUT_MS);
|
|
1297
1311
|
})
|
|
1298
1312
|
]);
|
|
1299
1313
|
clearTimeout(timer);
|
|
@@ -3636,7 +3650,7 @@ var SequentialCaptureStallError = class extends Error {
|
|
|
3636
3650
|
}
|
|
3637
3651
|
};
|
|
3638
3652
|
function raceAgainstStall(promise, deadlineMs, input, signal) {
|
|
3639
|
-
return new Promise((
|
|
3653
|
+
return new Promise((resolve11, reject) => {
|
|
3640
3654
|
const timer = setTimeout(
|
|
3641
3655
|
() => {
|
|
3642
3656
|
reject(
|
|
@@ -3655,7 +3669,7 @@ function raceAgainstStall(promise, deadlineMs, input, signal) {
|
|
|
3655
3669
|
promise.then(
|
|
3656
3670
|
(value) => {
|
|
3657
3671
|
clearTimeout(timer);
|
|
3658
|
-
|
|
3672
|
+
resolve11(value);
|
|
3659
3673
|
},
|
|
3660
3674
|
(error) => {
|
|
3661
3675
|
clearTimeout(timer);
|
|
@@ -5583,15 +5597,15 @@ function countAuthoredTimedClips(html) {
|
|
|
5583
5597
|
}
|
|
5584
5598
|
|
|
5585
5599
|
// src/services/render/stages/compileStage.ts
|
|
5586
|
-
import { join as
|
|
5600
|
+
import { join as join16 } from "node:path";
|
|
5587
5601
|
import { suggestMatchingPreset } from "@hyperframes/core";
|
|
5588
5602
|
|
|
5589
5603
|
// src/services/htmlCompiler.ts
|
|
5590
5604
|
import { decodedUrlPath } from "@hyperframes/parsers/asset-paths";
|
|
5591
|
-
import { createHash as
|
|
5592
|
-
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as
|
|
5593
|
-
import { join as
|
|
5594
|
-
import { parseHTML as
|
|
5605
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
5606
|
+
import { createReadStream as createReadStream2, existsSync as existsSync10, mkdirSync as mkdirSync7, readFileSync as readFileSync7 } from "fs";
|
|
5607
|
+
import { join as join15, dirname as dirname7, resolve as resolve7, basename as basename4, relative as relative2 } from "path";
|
|
5608
|
+
import { parseHTML as parseHTML5 } from "linkedom";
|
|
5595
5609
|
import {
|
|
5596
5610
|
COMPOSITION_SOURCE_URL,
|
|
5597
5611
|
compileTimingAttrs,
|
|
@@ -5629,6 +5643,8 @@ import {
|
|
|
5629
5643
|
readDeclaredDefaults,
|
|
5630
5644
|
parseHostVariableValues,
|
|
5631
5645
|
headStyleRuns,
|
|
5646
|
+
isAlwaysAppliedStyle,
|
|
5647
|
+
dedupeFontFaceRules,
|
|
5632
5648
|
inlineScriptRuns,
|
|
5633
5649
|
parsesAsScript,
|
|
5634
5650
|
styleElementsFor,
|
|
@@ -5865,7 +5881,7 @@ function readLoopOverride(el) {
|
|
|
5865
5881
|
}
|
|
5866
5882
|
return null;
|
|
5867
5883
|
}
|
|
5868
|
-
function
|
|
5884
|
+
function resolveLocalImageSourcePath(src, options) {
|
|
5869
5885
|
const trimmed = src.trim();
|
|
5870
5886
|
if (!trimmed || trimmed.startsWith("data:")) return null;
|
|
5871
5887
|
const { basePath } = splitUrlSuffix(trimmed);
|
|
@@ -5921,12 +5937,12 @@ function resolveClipWindowSeconds(img, compositionDuration) {
|
|
|
5921
5937
|
}
|
|
5922
5938
|
return null;
|
|
5923
5939
|
}
|
|
5924
|
-
function resolvePreparedPlayback(metadata,
|
|
5940
|
+
function resolvePreparedPlayback(metadata, loopOverride, windowSeconds) {
|
|
5925
5941
|
const gifDuration = metadata.durationSeconds > 0 ? metadata.durationSeconds : null;
|
|
5926
5942
|
let loopIterations = 1;
|
|
5927
|
-
if (
|
|
5943
|
+
if (resolveLoop(metadata, loopOverride)) {
|
|
5928
5944
|
loopIterations = windowSeconds != null && gifDuration != null ? Math.min(MAX_LOOP_ITERATIONS, Math.max(1, Math.ceil(windowSeconds / gifDuration))) : 1;
|
|
5929
|
-
} else if (metadata.loopCount != null && metadata.loopCount > 1) {
|
|
5945
|
+
} else if (loopOverride == null && metadata.loopCount != null && metadata.loopCount > 1) {
|
|
5930
5946
|
loopIterations = Math.min(MAX_LOOP_ITERATIONS, metadata.loopCount);
|
|
5931
5947
|
}
|
|
5932
5948
|
let padSeconds = 0;
|
|
@@ -6088,14 +6104,19 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6088
6104
|
const img = images[i];
|
|
6089
6105
|
const src = (img.getAttribute("src") || "").trim();
|
|
6090
6106
|
if (!hasGifExtension(src)) continue;
|
|
6091
|
-
const sourcePath =
|
|
6107
|
+
const sourcePath = resolveLocalImageSourcePath(src, options);
|
|
6092
6108
|
if (!sourcePath) continue;
|
|
6093
6109
|
const bytes = readFileSync5(sourcePath);
|
|
6094
6110
|
const metadata = parseAnimatedGifMetadata(bytes);
|
|
6095
6111
|
if (!metadata?.animated) continue;
|
|
6096
|
-
const
|
|
6112
|
+
const loopOverride = readLoopOverride(img);
|
|
6113
|
+
const loop = resolveLoop(metadata, loopOverride);
|
|
6097
6114
|
const windowSeconds = resolveClipWindowSeconds(img, compositionDuration);
|
|
6098
|
-
const { loopIterations, padSeconds } = resolvePreparedPlayback(
|
|
6115
|
+
const { loopIterations, padSeconds } = resolvePreparedPlayback(
|
|
6116
|
+
metadata,
|
|
6117
|
+
loopOverride,
|
|
6118
|
+
windowSeconds
|
|
6119
|
+
);
|
|
6099
6120
|
const hash = computePreparedGifHash(bytes, loopIterations, padSeconds);
|
|
6100
6121
|
const filename = `${CACHE_SCHEMA}-${hash.slice(0, 24)}.webm`;
|
|
6101
6122
|
const cachePath = join13(cacheDir, filename);
|
|
@@ -6131,19 +6152,74 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6131
6152
|
};
|
|
6132
6153
|
}
|
|
6133
6154
|
|
|
6134
|
-
// src/services/
|
|
6135
|
-
import {
|
|
6136
|
-
import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
|
|
6137
|
-
|
|
6138
|
-
// src/services/assetMediaType.ts
|
|
6139
|
-
import { existsSync as existsSync9 } from "node:fs";
|
|
6155
|
+
// src/services/heifImagePrep.ts
|
|
6156
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
6140
6157
|
import {
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6158
|
+
closeSync as closeSync4,
|
|
6159
|
+
copyFileSync as copyFileSync3,
|
|
6160
|
+
mkdirSync as mkdirSync6,
|
|
6161
|
+
openSync as openSync4,
|
|
6162
|
+
readFileSync as readFileSync6,
|
|
6163
|
+
readSync as readSync3,
|
|
6164
|
+
rmSync as rmSync8,
|
|
6165
|
+
statSync as statSync6,
|
|
6166
|
+
writeFileSync as writeFileSync4
|
|
6167
|
+
} from "node:fs";
|
|
6168
|
+
import { dirname as dirname6, join as join14, resolve as resolve6 } from "node:path";
|
|
6169
|
+
import { parseHTML as parseHTML4 } from "linkedom";
|
|
6170
|
+
import { renameWithRetry as renameWithRetry4, runFfmpeg as runFfmpeg3, runFfprobe } from "@hyperframes/engine";
|
|
6171
|
+
|
|
6172
|
+
// src/services/heifRotation.ts
|
|
6173
|
+
function* boxes(buf, start, end) {
|
|
6174
|
+
let at = start;
|
|
6175
|
+
while (at + 8 <= end) {
|
|
6176
|
+
let size = buf.readUInt32BE(at);
|
|
6177
|
+
let header = 8;
|
|
6178
|
+
if (size === 1 && at + 16 <= end) {
|
|
6179
|
+
size = Number(buf.readBigUInt64BE(at + 8));
|
|
6180
|
+
header = 16;
|
|
6181
|
+
} else if (size === 0) {
|
|
6182
|
+
size = end - at;
|
|
6183
|
+
}
|
|
6184
|
+
if (size < header || at + size > end) return;
|
|
6185
|
+
yield { type: buf.toString("latin1", at + 4, at + 8), start: at + header, end: at + size };
|
|
6186
|
+
at += size;
|
|
6187
|
+
}
|
|
6188
|
+
}
|
|
6189
|
+
function child(buf, parent, type, skip = 0) {
|
|
6190
|
+
return [...boxes(buf, parent.start + skip, parent.end)].filter((box) => box.type === type);
|
|
6191
|
+
}
|
|
6192
|
+
function readHeifPrimaryRotation(buf) {
|
|
6193
|
+
try {
|
|
6194
|
+
const root = { type: "", start: 0, end: buf.length };
|
|
6195
|
+
const meta = child(buf, root, "meta")[0];
|
|
6196
|
+
const pitm = meta && child(buf, meta, "pitm", 4)[0];
|
|
6197
|
+
const iprp = meta && child(buf, meta, "iprp", 4)[0];
|
|
6198
|
+
const ipco = iprp && child(buf, iprp, "ipco")[0];
|
|
6199
|
+
if (!pitm || !ipco || !iprp) return 0;
|
|
6200
|
+
const primaryId = buf[pitm.start] === 0 ? buf.readUInt16BE(pitm.start + 4) : buf.readUInt32BE(pitm.start + 4);
|
|
6201
|
+
const props = [...boxes(buf, ipco.start, ipco.end)];
|
|
6202
|
+
for (const ipma of child(buf, iprp, "ipma")) {
|
|
6203
|
+
const version = buf[ipma.start];
|
|
6204
|
+
const wide = (buf[ipma.start + 3] & 1) === 1;
|
|
6205
|
+
let at = ipma.start + 8;
|
|
6206
|
+
for (let n = buf.readUInt32BE(ipma.start + 4); n > 0 && at < ipma.end; n--) {
|
|
6207
|
+
const id = version < 1 ? buf.readUInt16BE(at) : buf.readUInt32BE(at);
|
|
6208
|
+
at += version < 1 ? 2 : 4;
|
|
6209
|
+
const count = buf[at++];
|
|
6210
|
+
for (let i = 0; i < count; i++) {
|
|
6211
|
+
const index = wide ? buf.readUInt16BE(at) & 32767 : buf[at] & 127;
|
|
6212
|
+
at += wide ? 2 : 1;
|
|
6213
|
+
const prop = props[index - 1];
|
|
6214
|
+
if (id === primaryId && prop?.type === "irot")
|
|
6215
|
+
return (buf[prop.start] & 3) * 90;
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
} catch {
|
|
6220
|
+
}
|
|
6221
|
+
return 0;
|
|
6222
|
+
}
|
|
6147
6223
|
|
|
6148
6224
|
// src/utils/semaphore.ts
|
|
6149
6225
|
var Semaphore = class {
|
|
@@ -6158,10 +6234,10 @@ var Semaphore = class {
|
|
|
6158
6234
|
this.active++;
|
|
6159
6235
|
return () => this.release();
|
|
6160
6236
|
}
|
|
6161
|
-
return new Promise((
|
|
6237
|
+
return new Promise((resolve11) => {
|
|
6162
6238
|
this.queue.push(() => {
|
|
6163
6239
|
this.active++;
|
|
6164
|
-
|
|
6240
|
+
resolve11(() => this.release());
|
|
6165
6241
|
});
|
|
6166
6242
|
});
|
|
6167
6243
|
}
|
|
@@ -6192,7 +6268,229 @@ async function withMediaProbeSlot(operation) {
|
|
|
6192
6268
|
}
|
|
6193
6269
|
}
|
|
6194
6270
|
|
|
6271
|
+
// src/services/heifImagePrep.ts
|
|
6272
|
+
var PREPARED_HEIF_SUBDIR = "_heif_images";
|
|
6273
|
+
var CACHE_SCHEMA2 = "hfheif-v1";
|
|
6274
|
+
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
6275
|
+
var HEIF_IMAGE_CONVERSION_FAILED = "HEIF_IMAGE_CONVERSION_FAILED";
|
|
6276
|
+
var HEIF_TOOL_UNAVAILABLE = "HEIF_TOOL_UNAVAILABLE";
|
|
6277
|
+
var HEIF_BRANDS = /* @__PURE__ */ new Set(["heic", "heix", "hevc", "hevx", "heim", "heis", "mif1", "msf1"]);
|
|
6278
|
+
var AVIF_BRANDS = /* @__PURE__ */ new Set(["avif", "avis"]);
|
|
6279
|
+
var DATA_URL_RE = /^data:image\/(?:heic|heif)(?:-sequence)?;base64,(.*)$/is;
|
|
6280
|
+
var HeifImageConversionError = class extends Error {
|
|
6281
|
+
code = HEIF_IMAGE_CONVERSION_FAILED;
|
|
6282
|
+
owner = "user";
|
|
6283
|
+
retryable = false;
|
|
6284
|
+
constructor(label, reason) {
|
|
6285
|
+
super(
|
|
6286
|
+
`Could not convert HEIC/HEIF image ${label} to JPEG: ${reason}. Re-export the photo as JPEG or PNG and update its src.`
|
|
6287
|
+
);
|
|
6288
|
+
this.name = "HeifImageConversionError";
|
|
6289
|
+
}
|
|
6290
|
+
};
|
|
6291
|
+
var HeifToolUnavailableError = class extends Error {
|
|
6292
|
+
code = HEIF_TOOL_UNAVAILABLE;
|
|
6293
|
+
owner = "system";
|
|
6294
|
+
retryable = false;
|
|
6295
|
+
constructor(label, reason) {
|
|
6296
|
+
super(
|
|
6297
|
+
`Could not convert HEIC/HEIF image ${label}: ${reason}. Converting HEIC needs ffmpeg and ffprobe 7.0 or newer with the hevc decoder.`
|
|
6298
|
+
);
|
|
6299
|
+
this.name = "HeifToolUnavailableError";
|
|
6300
|
+
}
|
|
6301
|
+
};
|
|
6302
|
+
var TOOL_FAILURE_RE = /(?:ffmpeg|ffprobe) not found|ENOENT|Option not found|Unrecognized option|Unknown decoder|Decoder \S+ not found/i;
|
|
6303
|
+
function isHeifBytes(head) {
|
|
6304
|
+
const bytes = Buffer.from(head.buffer, head.byteOffset, head.byteLength);
|
|
6305
|
+
if (bytes.length < 16 || bytes.toString("ascii", 4, 8) !== "ftyp") return false;
|
|
6306
|
+
const boxEnd = Math.min(bytes.readUInt32BE(0), bytes.length);
|
|
6307
|
+
if (boxEnd < 16) return false;
|
|
6308
|
+
const brands = [bytes.toString("ascii", 8, 12)];
|
|
6309
|
+
for (let offset = 16; offset + 4 <= boxEnd; offset += 4) {
|
|
6310
|
+
brands.push(bytes.toString("ascii", offset, offset + 4));
|
|
6311
|
+
}
|
|
6312
|
+
return !brands.some((b) => AVIF_BRANDS.has(b)) && brands.some((b) => HEIF_BRANDS.has(b));
|
|
6313
|
+
}
|
|
6314
|
+
function readHead(path) {
|
|
6315
|
+
let fd;
|
|
6316
|
+
try {
|
|
6317
|
+
fd = openSync4(path, "r");
|
|
6318
|
+
const buf = Buffer.alloc(4096);
|
|
6319
|
+
return buf.subarray(0, readSync3(fd, buf, 0, buf.length, 0));
|
|
6320
|
+
} catch {
|
|
6321
|
+
return null;
|
|
6322
|
+
} finally {
|
|
6323
|
+
if (fd !== void 0) closeSync4(fd);
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
async function probeJson(path, withGroups) {
|
|
6327
|
+
const stdout = await runFfprobe(
|
|
6328
|
+
path,
|
|
6329
|
+
withGroups ? ["-show_stream_groups", "-show_streams", "-of", "json"] : ["-show_streams", "-of", "json"]
|
|
6330
|
+
);
|
|
6331
|
+
return JSON.parse(stdout);
|
|
6332
|
+
}
|
|
6333
|
+
var ROTATION_FILTER = {
|
|
6334
|
+
0: "",
|
|
6335
|
+
90: "transpose=2",
|
|
6336
|
+
180: "hflip,vflip",
|
|
6337
|
+
270: "transpose=1"
|
|
6338
|
+
};
|
|
6339
|
+
function primaryGrid(probed) {
|
|
6340
|
+
const groups = probed.stream_groups ?? [];
|
|
6341
|
+
return (groups.find((g) => g.disposition?.default === 1) ?? groups[0])?.components?.[0];
|
|
6342
|
+
}
|
|
6343
|
+
async function convertHeifToJpeg(request) {
|
|
6344
|
+
const probed = await probeJson(request.inputPath, true);
|
|
6345
|
+
const grid = primaryGrid(probed);
|
|
6346
|
+
const rotation = readHeifPrimaryRotation(readFileSync6(request.inputPath));
|
|
6347
|
+
const filters = [];
|
|
6348
|
+
let inputs = [];
|
|
6349
|
+
let size = probed.streams?.[0];
|
|
6350
|
+
if (grid?.subcomponents?.length) {
|
|
6351
|
+
size = grid;
|
|
6352
|
+
const layout = grid.subcomponents.map(
|
|
6353
|
+
(t) => `${t.tile_horizontal_offset}_${t.tile_vertical_offset}`
|
|
6354
|
+
);
|
|
6355
|
+
inputs = grid.subcomponents.map((t) => `[0:${t.stream_index}]`);
|
|
6356
|
+
filters.push(
|
|
6357
|
+
`${inputs.join("")}xstack=inputs=${inputs.length}:layout=${layout.join("|")}`,
|
|
6358
|
+
`crop=${grid.width}:${grid.height}:${grid.horizontal_offset ?? 0}:${grid.vertical_offset ?? 0}`
|
|
6359
|
+
);
|
|
6360
|
+
}
|
|
6361
|
+
if (!size?.width || !size.height) throw new Error("no declared image size");
|
|
6362
|
+
if (rotation && grid) filters.push(ROTATION_FILTER[rotation]);
|
|
6363
|
+
const mapArgs = filters.length > 0 ? ["-filter_complex", `${filters.join(",")}[v]`, "-map", "[v]"] : [];
|
|
6364
|
+
const result = await runFfmpeg3(
|
|
6365
|
+
[
|
|
6366
|
+
"-hide_banner",
|
|
6367
|
+
"-loglevel",
|
|
6368
|
+
"error",
|
|
6369
|
+
"-i",
|
|
6370
|
+
request.inputPath,
|
|
6371
|
+
...mapArgs,
|
|
6372
|
+
"-frames:v",
|
|
6373
|
+
"1",
|
|
6374
|
+
"-q:v",
|
|
6375
|
+
"2",
|
|
6376
|
+
"-f",
|
|
6377
|
+
"mjpeg",
|
|
6378
|
+
"-y",
|
|
6379
|
+
request.outputPath
|
|
6380
|
+
],
|
|
6381
|
+
{ timeout: request.timeoutMs ?? DEFAULT_TIMEOUT_MS }
|
|
6382
|
+
);
|
|
6383
|
+
if (!result.success) {
|
|
6384
|
+
throw result.error ?? new Error(`ffmpeg exited ${result.exitCode}: ${result.stderr.slice(-300)}`);
|
|
6385
|
+
}
|
|
6386
|
+
const out = (await probeJson(request.outputPath, false)).streams?.[0];
|
|
6387
|
+
const same = out?.width === size.width && out.height === size.height;
|
|
6388
|
+
const swapped = out?.width === size.height && out.height === size.width;
|
|
6389
|
+
if (!same && !swapped) {
|
|
6390
|
+
throw new Error(
|
|
6391
|
+
`wrong size: decoded ${out?.width}x${out?.height}, file declares ${size.width}x${size.height}`
|
|
6392
|
+
);
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
function resolveImageSource(src, options) {
|
|
6396
|
+
const data = DATA_URL_RE.exec(src);
|
|
6397
|
+
if (data) {
|
|
6398
|
+
const bytes = Buffer.from(data[1], "base64");
|
|
6399
|
+
return isHeifBytes(bytes.subarray(0, 4096)) ? { path: null, bytes, label: '"inline image"' } : null;
|
|
6400
|
+
}
|
|
6401
|
+
const path = resolveLocalImageSourcePath(src, options);
|
|
6402
|
+
const head = path ? readHead(path) : null;
|
|
6403
|
+
if (!path || !head || !isHeifBytes(head)) return null;
|
|
6404
|
+
return { path, bytes: readFileSync6(path), label: `"${splitUrlSuffix(src).basePath}"` };
|
|
6405
|
+
}
|
|
6406
|
+
function isUsableFile2(path) {
|
|
6407
|
+
try {
|
|
6408
|
+
return statSync6(path).size > 0;
|
|
6409
|
+
} catch {
|
|
6410
|
+
return false;
|
|
6411
|
+
}
|
|
6412
|
+
}
|
|
6413
|
+
async function ensurePreparedJpeg(source, cachePath, outputPath, options) {
|
|
6414
|
+
mkdirSync6(dirname6(cachePath), { recursive: true });
|
|
6415
|
+
mkdirSync6(dirname6(outputPath), { recursive: true });
|
|
6416
|
+
if (!isUsableFile2(cachePath)) {
|
|
6417
|
+
const tmpPath = `${cachePath}.tmp-${process.pid}-${Date.now()}`;
|
|
6418
|
+
const inlinePath = `${tmpPath}.heic`;
|
|
6419
|
+
const inputPath = source.path ?? inlinePath;
|
|
6420
|
+
try {
|
|
6421
|
+
if (!source.path) writeFileSync4(inlinePath, source.bytes);
|
|
6422
|
+
await (options.transcode ?? convertHeifToJpeg)({
|
|
6423
|
+
inputPath,
|
|
6424
|
+
outputPath: tmpPath,
|
|
6425
|
+
timeoutMs: options.timeoutMs
|
|
6426
|
+
});
|
|
6427
|
+
if (!isUsableFile2(tmpPath)) throw new Error("conversion produced an empty file");
|
|
6428
|
+
renameWithRetry4(tmpPath, cachePath);
|
|
6429
|
+
} catch (error) {
|
|
6430
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
6431
|
+
throw TOOL_FAILURE_RE.test(reason) ? new HeifToolUnavailableError(source.label, reason) : new HeifImageConversionError(source.label, reason);
|
|
6432
|
+
} finally {
|
|
6433
|
+
rmSync8(tmpPath, { force: true });
|
|
6434
|
+
rmSync8(inlinePath, { force: true });
|
|
6435
|
+
}
|
|
6436
|
+
}
|
|
6437
|
+
if (resolve6(outputPath) !== resolve6(cachePath) && !isUsableFile2(outputPath)) {
|
|
6438
|
+
copyFileSync3(cachePath, outputPath);
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
async function prepareHeifImageInputs(html, options) {
|
|
6442
|
+
const outputDir = options.outputDir ?? join14(options.downloadDir, PREPARED_HEIF_SUBDIR);
|
|
6443
|
+
const outputSrcPrefix = normalizeRelPath(options.outputSrcPrefix ?? PREPARED_HEIF_SUBDIR);
|
|
6444
|
+
const cacheDir = options.cacheDir ?? outputDir;
|
|
6445
|
+
const preparedAssets = /* @__PURE__ */ new Map();
|
|
6446
|
+
const preparedImages = [];
|
|
6447
|
+
if (!/<img\b/i.test(html)) return { html, preparedAssets, preparedImages };
|
|
6448
|
+
const { document: document2 } = parseHTML4(html);
|
|
6449
|
+
const found = [];
|
|
6450
|
+
for (const img of Array.from(document2.querySelectorAll("img[src]"))) {
|
|
6451
|
+
const src = (img.getAttribute("src") || "").trim();
|
|
6452
|
+
const source = src ? resolveImageSource(src, options) : null;
|
|
6453
|
+
if (!source) continue;
|
|
6454
|
+
const hash = createHash5("sha256").update(CACHE_SCHEMA2).update("\0").update(source.bytes).digest("hex");
|
|
6455
|
+
found.push({ img, src, source, filename: `${CACHE_SCHEMA2}-${hash.slice(0, 24)}.jpg` });
|
|
6456
|
+
}
|
|
6457
|
+
if (found.length === 0) return { html, preparedAssets, preparedImages };
|
|
6458
|
+
const unique = new Map(found.map((item) => [item.filename, item.source]));
|
|
6459
|
+
const failed = /* @__PURE__ */ new Set();
|
|
6460
|
+
await Promise.all(
|
|
6461
|
+
[...unique].map(
|
|
6462
|
+
([filename, source]) => withMediaProbeSlot(
|
|
6463
|
+
() => ensurePreparedJpeg(source, join14(cacheDir, filename), join14(outputDir, filename), options)
|
|
6464
|
+
).catch((error) => {
|
|
6465
|
+
if (!options.onFailure) throw error;
|
|
6466
|
+
failed.add(filename);
|
|
6467
|
+
options.onFailure(error);
|
|
6468
|
+
})
|
|
6469
|
+
)
|
|
6470
|
+
);
|
|
6471
|
+
for (const { img, src, filename } of found) {
|
|
6472
|
+
if (failed.has(filename)) continue;
|
|
6473
|
+
const outputSrc = `${outputSrcPrefix}/${filename}`;
|
|
6474
|
+
img.setAttribute("src", outputSrc);
|
|
6475
|
+
preparedAssets.set(outputSrc, join14(outputDir, filename));
|
|
6476
|
+
preparedImages.push({ sourceSrc: src, outputSrc, outputPath: join14(outputDir, filename) });
|
|
6477
|
+
}
|
|
6478
|
+
return { html: document2.toString(), preparedAssets, preparedImages };
|
|
6479
|
+
}
|
|
6480
|
+
|
|
6481
|
+
// src/services/htmlCompiler.ts
|
|
6482
|
+
import { createStudioPositionSeekReapplyScript } from "@hyperframes/studio-server/manual-edits-render-script";
|
|
6483
|
+
import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
|
|
6484
|
+
|
|
6195
6485
|
// src/services/assetMediaType.ts
|
|
6486
|
+
import { existsSync as existsSync9 } from "node:fs";
|
|
6487
|
+
import {
|
|
6488
|
+
fingerprintElementId,
|
|
6489
|
+
isNotMediaPayload,
|
|
6490
|
+
NotMediaPayloadError,
|
|
6491
|
+
probeMediaProfile,
|
|
6492
|
+
resolveProjectRelativeSrc as resolveProjectRelativeSrc2
|
|
6493
|
+
} from "@hyperframes/engine";
|
|
6196
6494
|
var ASSET_MEDIA_TYPE_MISMATCH = "ASSET_MEDIA_TYPE_MISMATCH";
|
|
6197
6495
|
var AssetMediaTypeMismatchError = class extends Error {
|
|
6198
6496
|
code = ASSET_MEDIA_TYPE_MISMATCH;
|
|
@@ -6302,7 +6600,7 @@ function logRemoteDownloadTelemetry(event) {
|
|
|
6302
6600
|
}
|
|
6303
6601
|
var INFERRED_MEDIA_DURATION_ATTR = "data-hf-inferred-duration";
|
|
6304
6602
|
function parseSubCompHtmlForValidity(html) {
|
|
6305
|
-
return
|
|
6603
|
+
return parseHTML5(html).document;
|
|
6306
6604
|
}
|
|
6307
6605
|
function endsWithSourceUrl(code) {
|
|
6308
6606
|
const trimmed = code.trimEnd();
|
|
@@ -6310,7 +6608,7 @@ function endsWithSourceUrl(code) {
|
|
|
6310
6608
|
return lastLine.startsWith("//# sourceURL=") || lastLine.startsWith("//@ sourceURL=");
|
|
6311
6609
|
}
|
|
6312
6610
|
function prepareCompositionScripts(html) {
|
|
6313
|
-
const { document: document2 } =
|
|
6611
|
+
const { document: document2 } = parseHTML5(html);
|
|
6314
6612
|
for (const el of document2.querySelectorAll("script:not([src])")) {
|
|
6315
6613
|
const isModule = (el.getAttribute("type") || "").trim().toLowerCase() === "module";
|
|
6316
6614
|
const runsAsScript = isModule || isJavaScriptType(el);
|
|
@@ -6347,14 +6645,14 @@ Check that each file referenced by data-composition-src contains valid HTML with
|
|
|
6347
6645
|
}
|
|
6348
6646
|
};
|
|
6349
6647
|
function assertSubCompositionsUsable(html, projectDir, visited = /* @__PURE__ */ new Set()) {
|
|
6350
|
-
const { document: document2 } =
|
|
6648
|
+
const { document: document2 } = parseHTML5(html);
|
|
6351
6649
|
const hosts = [...document2.querySelectorAll("[data-composition-src]")];
|
|
6352
6650
|
const problems = [];
|
|
6353
6651
|
for (const el of hosts) {
|
|
6354
6652
|
const srcPath = el.getAttribute("data-composition-src");
|
|
6355
6653
|
if (!srcPath) continue;
|
|
6356
6654
|
if (isUnresolvedAssetPlaceholder(srcPath)) continue;
|
|
6357
|
-
const filePath =
|
|
6655
|
+
const filePath = resolve7(projectDir, srcPath);
|
|
6358
6656
|
if (visited.has(filePath)) continue;
|
|
6359
6657
|
const read = readProjectFile(filePath);
|
|
6360
6658
|
if (read.kind === "missing") {
|
|
@@ -6410,7 +6708,7 @@ function runtimeRoot(document2) {
|
|
|
6410
6708
|
}
|
|
6411
6709
|
function detectRenderModeHints(html, pageMotionBlur) {
|
|
6412
6710
|
const reasons = [];
|
|
6413
|
-
const { document: document2 } =
|
|
6711
|
+
const { document: document2 } = parseHTML5(html);
|
|
6414
6712
|
if (document2.querySelector("canvas[layoutsubtree]")) {
|
|
6415
6713
|
reasons.push({
|
|
6416
6714
|
code: "htmlInCanvas",
|
|
@@ -6452,7 +6750,7 @@ function detectThreeDTransformUsage(html) {
|
|
|
6452
6750
|
}
|
|
6453
6751
|
var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
|
|
6454
6752
|
function detectAncestorBackgroundImage(html) {
|
|
6455
|
-
const { document: document2 } =
|
|
6753
|
+
const { document: document2 } = parseHTML5(html);
|
|
6456
6754
|
const root = document2.querySelector("[data-composition-id]");
|
|
6457
6755
|
if (!root) return false;
|
|
6458
6756
|
const ancestors = [];
|
|
@@ -6499,7 +6797,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
6499
6797
|
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
6500
6798
|
let filePath = src;
|
|
6501
6799
|
if (isHttpUrl(src)) {
|
|
6502
|
-
if (!existsSync10(downloadDir))
|
|
6800
|
+
if (!existsSync10(downloadDir)) mkdirSync7(downloadDir, { recursive: true });
|
|
6503
6801
|
try {
|
|
6504
6802
|
filePath = await downloadToTemp(src, downloadDir, void 0, void 0, void 0, {
|
|
6505
6803
|
onTelemetry: logRemoteDownloadTelemetry
|
|
@@ -6509,7 +6807,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6509
6807
|
}
|
|
6510
6808
|
} else {
|
|
6511
6809
|
filePath = decodedUrlPath(src);
|
|
6512
|
-
if (!filePath.startsWith("/")) filePath =
|
|
6810
|
+
if (!filePath.startsWith("/")) filePath = join15(baseDir, filePath);
|
|
6513
6811
|
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
6514
6812
|
}
|
|
6515
6813
|
if (!isHttpUrl(filePath) && !existsSync10(filePath)) {
|
|
@@ -6566,7 +6864,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6566
6864
|
function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
6567
6865
|
const unresolvedIds = new Set(unresolvedMedia.map((element) => element.id));
|
|
6568
6866
|
if (unresolvedIds.size === 0) return html;
|
|
6569
|
-
const { document: document2 } =
|
|
6867
|
+
const { document: document2 } = parseHTML5(html);
|
|
6570
6868
|
let changed = false;
|
|
6571
6869
|
for (const element of document2.querySelectorAll("video[data-var-src], audio[data-var-src]")) {
|
|
6572
6870
|
if (!unresolvedIds.has(element.id)) continue;
|
|
@@ -6637,13 +6935,13 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
|
|
|
6637
6935
|
}
|
|
6638
6936
|
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
6639
6937
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
6640
|
-
const { document: document2 } =
|
|
6938
|
+
const { document: document2 } = parseHTML5(html);
|
|
6641
6939
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
6642
6940
|
const workItems = [];
|
|
6643
6941
|
for (const el of compEls) {
|
|
6644
6942
|
const srcPath = el.getAttribute("data-composition-src");
|
|
6645
6943
|
if (!srcPath) continue;
|
|
6646
|
-
const filePath =
|
|
6944
|
+
const filePath = resolve7(projectDir, srcPath);
|
|
6647
6945
|
if (visited.has(filePath)) {
|
|
6648
6946
|
continue;
|
|
6649
6947
|
}
|
|
@@ -6660,7 +6958,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6660
6958
|
workItems.map(async (item) => {
|
|
6661
6959
|
const { html: compiledSub } = await compileHtmlFile(
|
|
6662
6960
|
rewriteUnresolvableGsapToCdn(item.rawSubHtml, projectDir, item.srcPath),
|
|
6663
|
-
|
|
6961
|
+
dirname7(item.filePath),
|
|
6664
6962
|
downloadDir,
|
|
6665
6963
|
void 0,
|
|
6666
6964
|
urlByAbsentLocalPath
|
|
@@ -6688,7 +6986,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6688
6986
|
return { subCompositions };
|
|
6689
6987
|
}
|
|
6690
6988
|
function promoteCssImportsToLinkTags(html) {
|
|
6691
|
-
const { document: document2 } =
|
|
6989
|
+
const { document: document2 } = parseHTML5(html);
|
|
6692
6990
|
const head = document2.querySelector("head");
|
|
6693
6991
|
if (!head) return html;
|
|
6694
6992
|
const importRe = /@import\s+url\(\s*['"]?([^'")\s]+)['"]?\s*\)\s*;?/gi;
|
|
@@ -6757,10 +7055,15 @@ var ProducerHostIdentityMap = class extends Map {
|
|
|
6757
7055
|
}
|
|
6758
7056
|
};
|
|
6759
7057
|
function coalesceHeadStylesAndBodyScripts(html) {
|
|
6760
|
-
const { document: document2 } =
|
|
7058
|
+
const { document: document2 } = parseHTML5(html);
|
|
6761
7059
|
const head = document2.querySelector("head");
|
|
6762
7060
|
const body = document2.querySelector("body");
|
|
6763
7061
|
if (!head && !body) return html;
|
|
7062
|
+
const liveStyles = [...document2.querySelectorAll("style")].filter(isAlwaysAppliedStyle);
|
|
7063
|
+
const dedupedStyles = dedupeFontFaceRules(liveStyles.map((el) => el.textContent || ""));
|
|
7064
|
+
liveStyles.forEach((el, i) => {
|
|
7065
|
+
el.textContent = dedupedStyles[i] ?? "";
|
|
7066
|
+
});
|
|
6764
7067
|
const styleEls = head ? Array.from(head.querySelectorAll("style")) : [];
|
|
6765
7068
|
const importRe = /@import\s+url\([^)]*\)\s*;|@import\s+["'][^"']+["']\s*;/gi;
|
|
6766
7069
|
for (const run of styleEls.length > 1 ? headStyleRuns(styleEls) : []) {
|
|
@@ -6803,7 +7106,7 @@ function coalesceHeadStylesAndBodyScripts(html) {
|
|
|
6803
7106
|
return document2.toString();
|
|
6804
7107
|
}
|
|
6805
7108
|
function inlineSubCompositions(html, subCompositions, projectDir, variableOverrides = {}) {
|
|
6806
|
-
const { document: document2 } =
|
|
7109
|
+
const { document: document2 } = parseHTML5(html);
|
|
6807
7110
|
const head = document2.querySelector("head");
|
|
6808
7111
|
const body = document2.querySelector("body");
|
|
6809
7112
|
const hosts = Array.from(document2.querySelectorAll("[data-composition-src]"));
|
|
@@ -6832,17 +7135,17 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
6832
7135
|
resolveHtml: (srcPath) => {
|
|
6833
7136
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
6834
7137
|
if (!compHtml) {
|
|
6835
|
-
const read = readProjectFile(
|
|
7138
|
+
const read = readProjectFile(resolve7(projectDir, srcPath));
|
|
6836
7139
|
if (read.kind === "file") {
|
|
6837
7140
|
compHtml = rewriteUnresolvableGsapToCdn(read.text, projectDir, srcPath);
|
|
6838
7141
|
}
|
|
6839
7142
|
}
|
|
6840
7143
|
return compHtml;
|
|
6841
7144
|
},
|
|
6842
|
-
parseHtml: (htmlStr) =>
|
|
7145
|
+
parseHtml: (htmlStr) => parseHTML5(htmlStr).document,
|
|
6843
7146
|
// Mirrors the preview bundler: a sub-composition's SIBLING assets resolve
|
|
6844
7147
|
// against its own directory, project-root refs stay as authored.
|
|
6845
|
-
assetExists: (path) => existsSync10(
|
|
7148
|
+
assetExists: (path) => existsSync10(resolve7(projectDir, path)),
|
|
6846
7149
|
// Preserve the authored root wrapper as a child of the host, matching
|
|
6847
7150
|
// the preview bundler's shape (htmlBundler.ts's prepareFlattenedInnerRoot,
|
|
6848
7151
|
// which the runtime compositionLoader mirrors with its own copy for the
|
|
@@ -6943,7 +7246,7 @@ ${html}
|
|
|
6943
7246
|
</html>`;
|
|
6944
7247
|
}
|
|
6945
7248
|
function injectTextRenderingRule(html) {
|
|
6946
|
-
const { document: document2 } =
|
|
7249
|
+
const { document: document2 } = parseHTML5(html);
|
|
6947
7250
|
const head = document2.querySelector("head");
|
|
6948
7251
|
if (!head) return html;
|
|
6949
7252
|
if (document2.querySelector("style[data-hyperframes-text-rendering]")) {
|
|
@@ -6967,7 +7270,7 @@ function matchesScriptIntegrity(bytes, metadata) {
|
|
|
6967
7270
|
(alg) => hashes.some((hash) => hash[1]?.toLowerCase() === alg)
|
|
6968
7271
|
);
|
|
6969
7272
|
if (!strongest) return true;
|
|
6970
|
-
const actual =
|
|
7273
|
+
const actual = createHash6(strongest).update(bytes).digest();
|
|
6971
7274
|
return hashes.some(
|
|
6972
7275
|
(hash) => hash[1]?.toLowerCase() === strongest && actual.equals(Buffer.from(hash[2], "base64"))
|
|
6973
7276
|
);
|
|
@@ -6975,7 +7278,7 @@ function matchesScriptIntegrity(bytes, metadata) {
|
|
|
6975
7278
|
async function inlineExternalScripts(html) {
|
|
6976
7279
|
const fullHtml = ensureFullDocument(html);
|
|
6977
7280
|
const wrappedFragment = fullHtml !== html;
|
|
6978
|
-
const { document: document2 } =
|
|
7281
|
+
const { document: document2 } = parseHTML5(fullHtml);
|
|
6979
7282
|
const scripts = document2.querySelectorAll("script[src]");
|
|
6980
7283
|
const externalScripts = [];
|
|
6981
7284
|
for (const el of scripts) {
|
|
@@ -7024,12 +7327,12 @@ ${safeText}
|
|
|
7024
7327
|
return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
|
|
7025
7328
|
}
|
|
7026
7329
|
function collectExternalAssets(html, projectDir) {
|
|
7027
|
-
const absProjectDir =
|
|
7330
|
+
const absProjectDir = resolve7(projectDir);
|
|
7028
7331
|
const externalAssets = /* @__PURE__ */ new Map();
|
|
7029
7332
|
function processPath(rawPath) {
|
|
7030
7333
|
const trimmed = rawPath.trim();
|
|
7031
7334
|
if (isNonRelativeUrl(trimmed)) return null;
|
|
7032
|
-
const absPath =
|
|
7335
|
+
const absPath = resolve7(absProjectDir, trimmed);
|
|
7033
7336
|
if (isPathInside2(absPath, absProjectDir)) {
|
|
7034
7337
|
return null;
|
|
7035
7338
|
}
|
|
@@ -7038,7 +7341,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
7038
7341
|
externalAssets.set(safeKey, absPath);
|
|
7039
7342
|
return safeKey;
|
|
7040
7343
|
}
|
|
7041
|
-
const { document: document2 } =
|
|
7344
|
+
const { document: document2 } = parseHTML5(html);
|
|
7042
7345
|
for (const el of document2.querySelectorAll("[src], [href]")) {
|
|
7043
7346
|
for (const attr of ["src", "href"]) {
|
|
7044
7347
|
const val = (el.getAttribute(attr) || "").trim();
|
|
@@ -7083,7 +7386,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
|
|
|
7083
7386
|
var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
7084
7387
|
async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
|
|
7085
7388
|
if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
|
|
7086
|
-
if (!existsSync10(remoteDir))
|
|
7389
|
+
if (!existsSync10(remoteDir)) mkdirSync7(remoteDir, { recursive: true });
|
|
7087
7390
|
const urlToLocal = /* @__PURE__ */ new Map();
|
|
7088
7391
|
await Promise.all(
|
|
7089
7392
|
[...urlSet].map(async (url) => {
|
|
@@ -7130,7 +7433,7 @@ async function localizeRemoteMediaSources(html, downloadDir) {
|
|
|
7130
7433
|
return downloadAndRewriteUrls(
|
|
7131
7434
|
urlSet,
|
|
7132
7435
|
html,
|
|
7133
|
-
|
|
7436
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7134
7437
|
"Remote media download failed",
|
|
7135
7438
|
"Localized remote media source(s)"
|
|
7136
7439
|
);
|
|
@@ -7145,7 +7448,7 @@ async function localizeRemoteImageSources(html, downloadDir) {
|
|
|
7145
7448
|
return downloadAndRewriteUrls(
|
|
7146
7449
|
urlSet,
|
|
7147
7450
|
html,
|
|
7148
|
-
|
|
7451
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7149
7452
|
"Remote image download failed",
|
|
7150
7453
|
"Localized remote image source(s)"
|
|
7151
7454
|
);
|
|
@@ -7161,7 +7464,7 @@ async function localizeRemoteBackgroundImages(html, downloadDir) {
|
|
|
7161
7464
|
return downloadAndRewriteUrls(
|
|
7162
7465
|
urlSet,
|
|
7163
7466
|
html,
|
|
7164
|
-
|
|
7467
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7165
7468
|
"Remote background-image download failed",
|
|
7166
7469
|
"Localized remote background-image(s)",
|
|
7167
7470
|
// Quoted url('..')/url("..") are rewritten by downloadAndRewriteUrls' default
|
|
@@ -7296,7 +7599,7 @@ async function localizeRemoteFontFaces(html, downloadDir) {
|
|
|
7296
7599
|
return downloadAndRewriteUrls(
|
|
7297
7600
|
urlSet,
|
|
7298
7601
|
processed,
|
|
7299
|
-
|
|
7602
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7300
7603
|
"Remote font download failed",
|
|
7301
7604
|
"Localized remote font face(s)",
|
|
7302
7605
|
(h, url, relPath) => h.replaceAll(`url(${url})`, `url("${relPath}")`)
|
|
@@ -7341,7 +7644,7 @@ async function embedLocalFontFaces(html, projectDir) {
|
|
|
7341
7644
|
while ((urlMatch = urlRe.exec(block)) !== null) {
|
|
7342
7645
|
const localPath = urlMatch[1];
|
|
7343
7646
|
if (!localPath || embeddedPaths.has(localPath)) continue;
|
|
7344
|
-
const absPath = localPath.startsWith("/") ? localPath :
|
|
7647
|
+
const absPath = localPath.startsWith("/") ? localPath : resolve7(projectDir, localPath);
|
|
7345
7648
|
if (!isPathInside2(absPath, projectDir)) continue;
|
|
7346
7649
|
const ext = absPath.match(/\.(woff2?|ttf|otf|ttc)$/i)?.[1]?.toLowerCase() ?? "ttf";
|
|
7347
7650
|
try {
|
|
@@ -7390,7 +7693,7 @@ function renderGsapDist() {
|
|
|
7390
7693
|
}
|
|
7391
7694
|
function rewriteUnresolvableGsapToCdn(html, projectDir, compSrcPath) {
|
|
7392
7695
|
let gsapDist;
|
|
7393
|
-
const exists = (path) => existsSync10(
|
|
7696
|
+
const exists = (path) => existsSync10(resolve7(projectDir, path));
|
|
7394
7697
|
return html.replace(
|
|
7395
7698
|
/(<script\b[^>]*\bsrc=["'])([^"']*gsap[^"']*\/dist\/([^"']+))(["'][^>]*>)/gi,
|
|
7396
7699
|
(full, prefix, src, file, suffix) => {
|
|
@@ -7406,8 +7709,8 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
7406
7709
|
if (!isPathInside2(htmlPath, projectDir)) return html;
|
|
7407
7710
|
const entryPath = relative2(projectDir, htmlPath).replace(/\\/g, "/");
|
|
7408
7711
|
if (!entryPath.includes("/")) return html;
|
|
7409
|
-
const { document: document2 } =
|
|
7410
|
-
const assetExists = (path) => existsSync10(
|
|
7712
|
+
const { document: document2 } = parseHTML5(html);
|
|
7713
|
+
const assetExists = (path) => existsSync10(resolve7(projectDir, path));
|
|
7411
7714
|
rewriteAssetPaths(
|
|
7412
7715
|
document2.querySelectorAll("[src], [href]"),
|
|
7413
7716
|
entryPath,
|
|
@@ -7429,7 +7732,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
7429
7732
|
}
|
|
7430
7733
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
7431
7734
|
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
7432
|
-
|
|
7735
|
+
readFileSync7(htmlPath, "utf-8"),
|
|
7433
7736
|
projectDir,
|
|
7434
7737
|
htmlPath
|
|
7435
7738
|
);
|
|
@@ -7499,12 +7802,26 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7499
7802
|
const { html: htmlWithLocalImages, remoteMediaAssets: remoteImageAssets } = await localizeRemoteImageSources(htmlWithLocalMedia, downloadDir);
|
|
7500
7803
|
const { html: htmlWithLocalBg, remoteMediaAssets: remoteBgAssets } = await localizeRemoteBackgroundImages(htmlWithLocalImages, downloadDir);
|
|
7501
7804
|
const { html: htmlWithLocalizedFonts, remoteMediaAssets: remoteFontAssets } = await localizeRemoteFontFaces(htmlWithLocalBg, downloadDir);
|
|
7805
|
+
const {
|
|
7806
|
+
html: htmlWithJpegs,
|
|
7807
|
+
preparedAssets: preparedHeifAssets,
|
|
7808
|
+
preparedImages: preparedHeifImages
|
|
7809
|
+
} = await prepareHeifImageInputs(htmlWithLocalizedFonts, {
|
|
7810
|
+
projectDir,
|
|
7811
|
+
downloadDir,
|
|
7812
|
+
cacheDir: options.animatedGifCacheDir,
|
|
7813
|
+
sourceAssets: remoteImageAssets,
|
|
7814
|
+
timeoutMs: options.ffmpegProcessTimeout
|
|
7815
|
+
});
|
|
7816
|
+
if (preparedHeifImages.length > 0) {
|
|
7817
|
+
defaultLogger.info(`[Compiler] Converted ${preparedHeifImages.length} HEIC image(s) to JPEG`);
|
|
7818
|
+
}
|
|
7502
7819
|
const gifSourceAssets = new Map(remoteImageAssets);
|
|
7503
7820
|
const {
|
|
7504
7821
|
html: htmlWithPreparedGifs,
|
|
7505
7822
|
preparedAssets: preparedGifAssets,
|
|
7506
7823
|
preparedGifs
|
|
7507
|
-
} = await prepareAnimatedGifInputs(
|
|
7824
|
+
} = await prepareAnimatedGifInputs(htmlWithJpegs, {
|
|
7508
7825
|
projectDir,
|
|
7509
7826
|
downloadDir,
|
|
7510
7827
|
cacheDir: options.animatedGifCacheDir,
|
|
@@ -7531,11 +7848,14 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7531
7848
|
for (const [relPath, absPath] of preparedGifAssets) {
|
|
7532
7849
|
externalAssets.set(relPath, absPath);
|
|
7533
7850
|
}
|
|
7851
|
+
for (const [relPath, absPath] of preparedHeifAssets) {
|
|
7852
|
+
externalAssets.set(relPath, absPath);
|
|
7853
|
+
}
|
|
7534
7854
|
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
|
7535
7855
|
const masterFxChain = parseMasterFxChain(html);
|
|
7536
7856
|
for (const video of videos) {
|
|
7537
7857
|
if (isHttpUrl(video.src)) continue;
|
|
7538
|
-
const videoPath =
|
|
7858
|
+
const videoPath = resolve7(projectDir, video.src);
|
|
7539
7859
|
const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
|
|
7540
7860
|
(async () => {
|
|
7541
7861
|
const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
|
|
@@ -7555,7 +7875,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7555
7875
|
})().catch(() => {
|
|
7556
7876
|
});
|
|
7557
7877
|
}
|
|
7558
|
-
const { document: document2 } =
|
|
7878
|
+
const { document: document2 } = parseHTML5(html);
|
|
7559
7879
|
const rootEl = document2.querySelector("[data-composition-id]");
|
|
7560
7880
|
const width = rootEl ? parseInt(rootEl.getAttribute("data-width") || "1080", 10) : 1080;
|
|
7561
7881
|
const height = rootEl ? parseInt(rootEl.getAttribute("data-height") || "1920", 10) : 1920;
|
|
@@ -7962,7 +8282,7 @@ async function runCompileStage(input) {
|
|
|
7962
8282
|
abortSignal
|
|
7963
8283
|
} = input;
|
|
7964
8284
|
const compileStart = Date.now();
|
|
7965
|
-
const compiled = await compileForRender(projectDir, htmlPath,
|
|
8285
|
+
const compiled = await compileForRender(projectDir, htmlPath, join16(workDir, "downloads"), {
|
|
7966
8286
|
log,
|
|
7967
8287
|
failClosedFontFetch: failClosedFontFetch === true,
|
|
7968
8288
|
allowSystemFontCapture,
|
|
@@ -7970,7 +8290,7 @@ async function runCompileStage(input) {
|
|
|
7970
8290
|
variables: input.variables,
|
|
7971
8291
|
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7972
8292
|
pageMotionBlur: input.pageMotionBlur,
|
|
7973
|
-
animatedGifCacheDir: cfg.extractCacheDir ?
|
|
8293
|
+
animatedGifCacheDir: cfg.extractCacheDir ? join16(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7974
8294
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7975
8295
|
});
|
|
7976
8296
|
assertNotAborted();
|
|
@@ -8026,7 +8346,7 @@ async function runCompileStage(input) {
|
|
|
8026
8346
|
};
|
|
8027
8347
|
await preflightCompositionAssetMediaTypes({
|
|
8028
8348
|
projectDir,
|
|
8029
|
-
compiledDir:
|
|
8349
|
+
compiledDir: join16(workDir, "compiled"),
|
|
8030
8350
|
composition,
|
|
8031
8351
|
signal: abortSignal
|
|
8032
8352
|
});
|
|
@@ -8071,8 +8391,8 @@ async function runCompileStage(input) {
|
|
|
8071
8391
|
}
|
|
8072
8392
|
|
|
8073
8393
|
// src/services/render/stages/probeStage.ts
|
|
8074
|
-
import { join as
|
|
8075
|
-
import { parseHTML as
|
|
8394
|
+
import { join as join17 } from "node:path";
|
|
8395
|
+
import { parseHTML as parseHTML6 } from "linkedom";
|
|
8076
8396
|
import {
|
|
8077
8397
|
closeCaptureSession as closeCaptureSession4,
|
|
8078
8398
|
compositionRequiresWebGpu,
|
|
@@ -8119,7 +8439,7 @@ function durationToFrameCount(duration, fps) {
|
|
|
8119
8439
|
}
|
|
8120
8440
|
function hasScriptedAudioVolumeAutomation(html, audioCount) {
|
|
8121
8441
|
if (audioCount <= 0) return false;
|
|
8122
|
-
const { document: document2 } =
|
|
8442
|
+
const { document: document2 } = parseHTML6(html);
|
|
8123
8443
|
const scriptBodies = [...document2.querySelectorAll("script")].map((script) => script.textContent ?? "").join("\n");
|
|
8124
8444
|
if (!scriptBodies) return false;
|
|
8125
8445
|
return /\.\s*volume\s*=/i.test(scriptBodies) || /\b(?:gsap|tl|timeline|tween)\s*\.\s*(?:to|fromTo|set)\s*\([\s\S]{0,2000}\bvolume\s*:/i.test(
|
|
@@ -8127,12 +8447,12 @@ function hasScriptedAudioVolumeAutomation(html, audioCount) {
|
|
|
8127
8447
|
);
|
|
8128
8448
|
}
|
|
8129
8449
|
function hasAutoStartVideos(html) {
|
|
8130
|
-
const { document: document2 } =
|
|
8450
|
+
const { document: document2 } = parseHTML6(html);
|
|
8131
8451
|
return document2.querySelector("video[data-hf-auto-start]") !== null;
|
|
8132
8452
|
}
|
|
8133
8453
|
function hasVariableBoundMedia(html, variables) {
|
|
8134
8454
|
if (!variables || Object.keys(variables).length === 0) return false;
|
|
8135
|
-
const { document: document2 } =
|
|
8455
|
+
const { document: document2 } = parseHTML6(html);
|
|
8136
8456
|
return Array.from(
|
|
8137
8457
|
document2.querySelectorAll(
|
|
8138
8458
|
"img[data-var-src], audio[data-var-src], video[data-var-src], source[data-var-src]"
|
|
@@ -8143,7 +8463,7 @@ function hasVariableBoundMedia(html, variables) {
|
|
|
8143
8463
|
});
|
|
8144
8464
|
}
|
|
8145
8465
|
function authoredMediaStarts(html) {
|
|
8146
|
-
const { document: document2 } =
|
|
8466
|
+
const { document: document2 } = parseHTML6(html);
|
|
8147
8467
|
const starts = /* @__PURE__ */ new Map();
|
|
8148
8468
|
for (const media of document2.querySelectorAll("video, audio")) {
|
|
8149
8469
|
const id = media.getAttribute(MEDIA_RENDER_ID_ATTR2) || media.getAttribute("id");
|
|
@@ -8180,7 +8500,7 @@ function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, dura
|
|
|
8180
8500
|
return existingEnd <= 0 ? projectedEnd : Math.min(existingEnd, projectedEnd);
|
|
8181
8501
|
}
|
|
8182
8502
|
function hasRuntimeMediaChanges(html) {
|
|
8183
|
-
const { document: document2 } =
|
|
8503
|
+
const { document: document2 } = parseHTML6(html);
|
|
8184
8504
|
const scriptBodies = [...document2.querySelectorAll("script")].map(
|
|
8185
8505
|
(script) => script.textContent ?? ""
|
|
8186
8506
|
);
|
|
@@ -8263,7 +8583,7 @@ async function runProbeStage(input) {
|
|
|
8263
8583
|
});
|
|
8264
8584
|
fileServer = await createFileServer({
|
|
8265
8585
|
projectDir,
|
|
8266
|
-
compiledDir:
|
|
8586
|
+
compiledDir: join17(workDir, "compiled"),
|
|
8267
8587
|
port: 0,
|
|
8268
8588
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
8269
8589
|
fps: job.config.fps
|
|
@@ -8292,7 +8612,7 @@ async function runProbeStage(input) {
|
|
|
8292
8612
|
log.info("Creating capture session...", { attempt, maxAttempts: PROBE_MAX_ATTEMPTS });
|
|
8293
8613
|
probeSession = await createCaptureSession4(
|
|
8294
8614
|
fileServer.url,
|
|
8295
|
-
|
|
8615
|
+
join17(workDir, "probe"),
|
|
8296
8616
|
captureOpts,
|
|
8297
8617
|
null,
|
|
8298
8618
|
probeCfg
|
|
@@ -8377,7 +8697,7 @@ async function runProbeStage(input) {
|
|
|
8377
8697
|
});
|
|
8378
8698
|
probeSession = await createCaptureSession4(
|
|
8379
8699
|
fileServer.url,
|
|
8380
|
-
|
|
8700
|
+
join17(workDir, "probe-screenshot"),
|
|
8381
8701
|
captureOpts,
|
|
8382
8702
|
null,
|
|
8383
8703
|
{ ...probeCfg, forceScreenshot: true }
|
|
@@ -8413,7 +8733,7 @@ async function runProbeStage(input) {
|
|
|
8413
8733
|
compiled,
|
|
8414
8734
|
resolutions,
|
|
8415
8735
|
projectDir,
|
|
8416
|
-
|
|
8736
|
+
join17(workDir, "downloads")
|
|
8417
8737
|
);
|
|
8418
8738
|
assertNotAborted();
|
|
8419
8739
|
composition.videos = compiled.videos;
|
|
@@ -8604,7 +8924,7 @@ async function runProbeStage(input) {
|
|
|
8604
8924
|
}
|
|
8605
8925
|
await preflightCompositionAssetMediaTypes({
|
|
8606
8926
|
projectDir,
|
|
8607
|
-
compiledDir:
|
|
8927
|
+
compiledDir: join17(workDir, "compiled"),
|
|
8608
8928
|
composition,
|
|
8609
8929
|
signal: abortSignal
|
|
8610
8930
|
});
|
|
@@ -8738,7 +9058,7 @@ function validateRenderDuration(input) {
|
|
|
8738
9058
|
|
|
8739
9059
|
// src/services/render/stages/extractVideosStage.ts
|
|
8740
9060
|
import { existsSync as existsSync11 } from "node:fs";
|
|
8741
|
-
import { join as
|
|
9061
|
+
import { join as join18 } from "node:path";
|
|
8742
9062
|
import {
|
|
8743
9063
|
classifyVideoExtractionError,
|
|
8744
9064
|
createFrameLookupTable,
|
|
@@ -9053,14 +9373,14 @@ async function runExtractVideosStage(input) {
|
|
|
9053
9373
|
// because short boundary counts can differ by one frame.
|
|
9054
9374
|
{
|
|
9055
9375
|
fps: job.config.fps,
|
|
9056
|
-
outputDir:
|
|
9376
|
+
outputDir: join18(compiledDir, "__hyperframes_video_frames"),
|
|
9057
9377
|
format: job.config.videoFrameFormat ?? "auto",
|
|
9058
9378
|
toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
|
|
9059
9379
|
timelineEnd: composition.duration,
|
|
9060
9380
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
9061
9381
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
9062
9382
|
deferRangeExtraction,
|
|
9063
|
-
contentKeyedDirs: [
|
|
9383
|
+
contentKeyedDirs: [join18(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
9064
9384
|
urlByAbsentLocalPath
|
|
9065
9385
|
},
|
|
9066
9386
|
abortSignal,
|
|
@@ -9127,8 +9447,8 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
|
|
|
9127
9447
|
}
|
|
9128
9448
|
|
|
9129
9449
|
// src/services/render/stages/audioStage.ts
|
|
9130
|
-
import { join as
|
|
9131
|
-
import { rmSync as
|
|
9450
|
+
import { join as join20 } from "node:path";
|
|
9451
|
+
import { rmSync as rmSync10 } from "node:fs";
|
|
9132
9452
|
import {
|
|
9133
9453
|
MIXED_AUDIO_FILENAME,
|
|
9134
9454
|
audioFailureDiagnostics,
|
|
@@ -9137,8 +9457,8 @@ import {
|
|
|
9137
9457
|
|
|
9138
9458
|
// src/services/render/audioPadTrim.ts
|
|
9139
9459
|
import { spawn } from "node:child_process";
|
|
9140
|
-
import { mkdtempSync as mkdtempSync3, rmSync as
|
|
9141
|
-
import { dirname as
|
|
9460
|
+
import { mkdtempSync as mkdtempSync3, rmSync as rmSync9 } from "node:fs";
|
|
9461
|
+
import { dirname as dirname8, join as join19 } from "node:path";
|
|
9142
9462
|
import {
|
|
9143
9463
|
DEFAULT_CONFIG,
|
|
9144
9464
|
describeFfmpegFailure as describeFfmpegFailure2,
|
|
@@ -9146,8 +9466,8 @@ import {
|
|
|
9146
9466
|
extractAudioMetadata as extractAudioMetadata3,
|
|
9147
9467
|
getFfprobeBinary,
|
|
9148
9468
|
ManagedChildProcess,
|
|
9149
|
-
renameWithRetry as
|
|
9150
|
-
runFfmpeg as
|
|
9469
|
+
renameWithRetry as renameWithRetry5,
|
|
9470
|
+
runFfmpeg as runFfmpeg5,
|
|
9151
9471
|
trackChildProcess
|
|
9152
9472
|
} from "@hyperframes/engine";
|
|
9153
9473
|
import { redactKnownPaths, redactTelemetryString as redactTelemetryString3 } from "@hyperframes/core";
|
|
@@ -9157,7 +9477,7 @@ import {
|
|
|
9157
9477
|
aacSegmentCount,
|
|
9158
9478
|
describeFfmpegFailure,
|
|
9159
9479
|
extractAudioMetadata as extractAudioMetadata2,
|
|
9160
|
-
runFfmpeg as
|
|
9480
|
+
runFfmpeg as runFfmpeg4
|
|
9161
9481
|
} from "@hyperframes/engine";
|
|
9162
9482
|
var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
9163
9483
|
function checkedDbfs(value) {
|
|
@@ -9181,7 +9501,7 @@ async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
|
|
|
9181
9501
|
else laterBlocks = Math.max(laterBlocks, block);
|
|
9182
9502
|
}
|
|
9183
9503
|
};
|
|
9184
|
-
const result = await
|
|
9504
|
+
const result = await runFfmpeg4(
|
|
9185
9505
|
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
|
|
9186
9506
|
{ signal, timeout: timeoutMs, onStderr: readLines }
|
|
9187
9507
|
);
|
|
@@ -9341,7 +9661,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
9341
9661
|
error: `audioPadTrim: failed to materialize ${plan.operation}: ${err instanceof Error ? err.message : String(err)}`
|
|
9342
9662
|
};
|
|
9343
9663
|
} finally {
|
|
9344
|
-
for (const path of plan.cleanupPaths)
|
|
9664
|
+
for (const path of plan.cleanupPaths) rmSync9(path, { force: true });
|
|
9345
9665
|
}
|
|
9346
9666
|
let loweredDb = 0;
|
|
9347
9667
|
if (probeTruePeak) {
|
|
@@ -9388,8 +9708,8 @@ async function constrainAacTruePeak(input) {
|
|
|
9388
9708
|
async function enforceAacTruePeak(input) {
|
|
9389
9709
|
let scratchDir;
|
|
9390
9710
|
try {
|
|
9391
|
-
scratchDir = mkdtempSync3(
|
|
9392
|
-
const correctedPath =
|
|
9711
|
+
scratchDir = mkdtempSync3(join19(dirname8(input.audioPath), ".true-peak-"));
|
|
9712
|
+
const correctedPath = join19(scratchDir, "audio.m4a");
|
|
9393
9713
|
const measurements = [];
|
|
9394
9714
|
let attenuationDb = 0;
|
|
9395
9715
|
let sourcePeakDbfs;
|
|
@@ -9404,7 +9724,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9404
9724
|
}
|
|
9405
9725
|
if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
|
|
9406
9726
|
if (measuredPath !== correctedPath) return { success: true };
|
|
9407
|
-
|
|
9727
|
+
renameWithRetry5(correctedPath, input.audioPath);
|
|
9408
9728
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
9409
9729
|
}
|
|
9410
9730
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
@@ -9445,7 +9765,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9445
9765
|
)}`
|
|
9446
9766
|
};
|
|
9447
9767
|
} finally {
|
|
9448
|
-
if (scratchDir)
|
|
9768
|
+
if (scratchDir) rmSync9(scratchDir, { recursive: true, force: true });
|
|
9449
9769
|
}
|
|
9450
9770
|
}
|
|
9451
9771
|
function failResult(outputPath, target, source, error) {
|
|
@@ -9522,7 +9842,7 @@ async function defaultProbeAudioInfo(audioPath, signal) {
|
|
|
9522
9842
|
};
|
|
9523
9843
|
}
|
|
9524
9844
|
async function defaultRunFfmpeg(args, timeoutMs, signal) {
|
|
9525
|
-
return toStepResult(await
|
|
9845
|
+
return toStepResult(await runFfmpeg5(args, { signal, timeout: timeoutMs }), timeoutMs);
|
|
9526
9846
|
}
|
|
9527
9847
|
function defaultAacEncoder(timeoutMs, signal, pcmSourcePath) {
|
|
9528
9848
|
return async (inputPath, outputPath, durationSeconds, filter) => {
|
|
@@ -9592,7 +9912,7 @@ function assertAudioNotInterrupted(failures) {
|
|
|
9592
9912
|
}
|
|
9593
9913
|
async function runAudioStage(input) {
|
|
9594
9914
|
const stageStart = Date.now();
|
|
9595
|
-
const pcmSourcePath =
|
|
9915
|
+
const pcmSourcePath = join20(input.workDir, "audio-source.wav");
|
|
9596
9916
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
9597
9917
|
try {
|
|
9598
9918
|
const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
|
|
@@ -9612,7 +9932,7 @@ async function runAudioStage(input) {
|
|
|
9612
9932
|
}
|
|
9613
9933
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
9614
9934
|
} finally {
|
|
9615
|
-
|
|
9935
|
+
rmSync10(pcmSourcePath, { force: true });
|
|
9616
9936
|
}
|
|
9617
9937
|
}
|
|
9618
9938
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
@@ -9630,7 +9950,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9630
9950
|
assertNotAborted,
|
|
9631
9951
|
log
|
|
9632
9952
|
} = input;
|
|
9633
|
-
const audioOutputPath =
|
|
9953
|
+
const audioOutputPath = join20(workDir, MIXED_AUDIO_FILENAME);
|
|
9634
9954
|
let hasAudio = false;
|
|
9635
9955
|
let audioError;
|
|
9636
9956
|
let audioFailures;
|
|
@@ -9641,7 +9961,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9641
9961
|
audioResult = await processCompositionAudio(
|
|
9642
9962
|
audios,
|
|
9643
9963
|
projectDir,
|
|
9644
|
-
|
|
9964
|
+
join20(workDir, "audio-work"),
|
|
9645
9965
|
audioOutputPath,
|
|
9646
9966
|
duration,
|
|
9647
9967
|
abortSignal,
|
|
@@ -9710,7 +10030,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9710
10030
|
|
|
9711
10031
|
// src/services/render/stages/captureStage.ts
|
|
9712
10032
|
import { existsSync as existsSync12, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
9713
|
-
import { join as
|
|
10033
|
+
import { join as join21 } from "node:path";
|
|
9714
10034
|
import {
|
|
9715
10035
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
9716
10036
|
frameFileExtension,
|
|
@@ -9783,13 +10103,13 @@ function measureCaptureFrameBytes(workDir, framesDir) {
|
|
|
9783
10103
|
if (!existsSync12(workDir)) return total;
|
|
9784
10104
|
for (const attempt of readdirSync3(workDir)) {
|
|
9785
10105
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
9786
|
-
total += sampleDirectoryBytes(
|
|
10106
|
+
total += sampleDirectoryBytes(join21(workDir, attempt));
|
|
9787
10107
|
continue;
|
|
9788
10108
|
}
|
|
9789
10109
|
if (!/^capture-attempt-\d+$/.test(attempt)) continue;
|
|
9790
|
-
const attemptDir =
|
|
10110
|
+
const attemptDir = join21(workDir, attempt);
|
|
9791
10111
|
for (const name of readdirSync3(attemptDir)) {
|
|
9792
|
-
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(
|
|
10112
|
+
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join21(attemptDir, name));
|
|
9793
10113
|
}
|
|
9794
10114
|
}
|
|
9795
10115
|
return total;
|
|
@@ -10082,8 +10402,8 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
10082
10402
|
}
|
|
10083
10403
|
|
|
10084
10404
|
// src/services/render/stages/captureHdrStage.ts
|
|
10085
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
10086
|
-
import { join as
|
|
10405
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "node:fs";
|
|
10406
|
+
import { join as join25 } from "node:path";
|
|
10087
10407
|
import {
|
|
10088
10408
|
closeCaptureSession as closeCaptureSession7,
|
|
10089
10409
|
cloneCaptureWarnings as cloneCaptureWarnings2,
|
|
@@ -10155,7 +10475,7 @@ function createHdrImageTransferCache(options = {}) {
|
|
|
10155
10475
|
}
|
|
10156
10476
|
|
|
10157
10477
|
// src/services/render/stages/captureHdrSequentialLoop.ts
|
|
10158
|
-
import { join as
|
|
10478
|
+
import { join as join22 } from "node:path";
|
|
10159
10479
|
import {
|
|
10160
10480
|
TRANSITIONS,
|
|
10161
10481
|
crossfade
|
|
@@ -10280,7 +10600,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
10280
10600
|
);
|
|
10281
10601
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
10282
10602
|
writeFileExclusiveSync(
|
|
10283
|
-
|
|
10603
|
+
join22(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
10284
10604
|
normalCanvas
|
|
10285
10605
|
);
|
|
10286
10606
|
}
|
|
@@ -10312,7 +10632,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
10312
10632
|
}
|
|
10313
10633
|
|
|
10314
10634
|
// src/services/render/stages/captureHdrHybridLoop.ts
|
|
10315
|
-
import { join as
|
|
10635
|
+
import { join as join24 } from "node:path";
|
|
10316
10636
|
import {
|
|
10317
10637
|
TRANSITIONS as TRANSITIONS2,
|
|
10318
10638
|
closeCaptureSession as closeCaptureSession6,
|
|
@@ -10326,7 +10646,7 @@ import {
|
|
|
10326
10646
|
// src/services/shaderTransitionWorkerPool.ts
|
|
10327
10647
|
import { Worker } from "node:worker_threads";
|
|
10328
10648
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
10329
|
-
import { dirname as
|
|
10649
|
+
import { dirname as dirname9, join as join23 } from "node:path";
|
|
10330
10650
|
import { createRequire } from "node:module";
|
|
10331
10651
|
import { existsSync as existsSync13 } from "node:fs";
|
|
10332
10652
|
import { cpus as cpus2 } from "node:os";
|
|
@@ -10339,10 +10659,10 @@ function resolveWorkerEntry(explicit) {
|
|
|
10339
10659
|
const isTs = override.endsWith(".ts");
|
|
10340
10660
|
return { path: override, isTs };
|
|
10341
10661
|
}
|
|
10342
|
-
const moduleDir =
|
|
10343
|
-
const jsPath =
|
|
10662
|
+
const moduleDir = dirname9(fileURLToPath3(import.meta.url));
|
|
10663
|
+
const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
|
|
10344
10664
|
if (existsSync13(jsPath)) return { path: jsPath, isTs: false };
|
|
10345
|
-
const tsPath =
|
|
10665
|
+
const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
|
|
10346
10666
|
return { path: tsPath, isTs: true };
|
|
10347
10667
|
}
|
|
10348
10668
|
function buildExecArgv(entryIsTs) {
|
|
@@ -10485,8 +10805,8 @@ async function createShaderTransitionWorkerPool(opts) {
|
|
|
10485
10805
|
if (terminated) {
|
|
10486
10806
|
throw new Error("shader-blend pool already terminated");
|
|
10487
10807
|
}
|
|
10488
|
-
return new Promise((
|
|
10489
|
-
const task = traceEnabled ? { req, resolve:
|
|
10808
|
+
return new Promise((resolve11, reject) => {
|
|
10809
|
+
const task = traceEnabled ? { req, resolve: resolve11, reject, enqueuedAtMs: Date.now(), traceId: ++nextTaskId } : { req, resolve: resolve11, reject };
|
|
10490
10810
|
const idle = slots.find((s) => !s.busy && !s.dead);
|
|
10491
10811
|
if (idle) {
|
|
10492
10812
|
queue.unshift(task);
|
|
@@ -10710,7 +11030,7 @@ async function runHybridLayeredFrameLoop(input) {
|
|
|
10710
11030
|
);
|
|
10711
11031
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
10712
11032
|
writeFileExclusiveSync(
|
|
10713
|
-
|
|
11033
|
+
join24(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
10714
11034
|
canvas
|
|
10715
11035
|
);
|
|
10716
11036
|
}
|
|
@@ -10898,9 +11218,9 @@ async function runCaptureHdrStage(input) {
|
|
|
10898
11218
|
if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
|
|
10899
11219
|
}
|
|
10900
11220
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
10901
|
-
const debugDumpDir = debugDumpEnabled ?
|
|
11221
|
+
const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
|
|
10902
11222
|
if (debugDumpDir && !existsSync14(debugDumpDir)) {
|
|
10903
|
-
|
|
11223
|
+
mkdirSync8(debugDumpDir, { recursive: true });
|
|
10904
11224
|
}
|
|
10905
11225
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
10906
11226
|
const hdrTargetTransfer = compositeTransfer === "srgb" ? void 0 : compositeTransfer;
|
|
@@ -11053,24 +11373,24 @@ async function runCaptureHdrStage(input) {
|
|
|
11053
11373
|
|
|
11054
11374
|
// src/services/render/stages/encodeStage.ts
|
|
11055
11375
|
import {
|
|
11056
|
-
copyFileSync as
|
|
11376
|
+
copyFileSync as copyFileSync4,
|
|
11057
11377
|
existsSync as existsSync15,
|
|
11058
|
-
mkdirSync as
|
|
11378
|
+
mkdirSync as mkdirSync9,
|
|
11059
11379
|
readdirSync as readdirSync4,
|
|
11060
|
-
readFileSync as
|
|
11061
|
-
rmSync as
|
|
11062
|
-
statSync as
|
|
11063
|
-
writeFileSync as
|
|
11380
|
+
readFileSync as readFileSync8,
|
|
11381
|
+
rmSync as rmSync11,
|
|
11382
|
+
statSync as statSync7,
|
|
11383
|
+
writeFileSync as writeFileSync5
|
|
11064
11384
|
} from "node:fs";
|
|
11065
11385
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
11066
|
-
import { dirname as
|
|
11386
|
+
import { dirname as dirname10, join as join26 } from "node:path";
|
|
11067
11387
|
import {
|
|
11068
11388
|
encodeFramesChunkedConcat,
|
|
11069
11389
|
encodeFramesFromDir,
|
|
11070
11390
|
formatFfmpegError,
|
|
11071
11391
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
|
|
11072
11392
|
resolveConfig,
|
|
11073
|
-
runFfmpeg as
|
|
11393
|
+
runFfmpeg as runFfmpeg6,
|
|
11074
11394
|
spawnStreamingEncoder as spawnStreamingEncoder4
|
|
11075
11395
|
} from "@hyperframes/engine";
|
|
11076
11396
|
import {
|
|
@@ -11146,17 +11466,17 @@ function gifEncodeFailure(startTime, outputPath, error, failureReason) {
|
|
|
11146
11466
|
}
|
|
11147
11467
|
async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
11148
11468
|
const { outputPath } = argsInput;
|
|
11149
|
-
const result = await
|
|
11469
|
+
const result = await runFfmpeg6(buildGifPaletteuseArgs({ ...argsInput, wholeFrames: true }), run);
|
|
11150
11470
|
if (!result.success) {
|
|
11151
11471
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
11152
11472
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11153
11473
|
}
|
|
11154
|
-
const gif =
|
|
11474
|
+
const gif = readFileSync8(outputPath);
|
|
11155
11475
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
11156
11476
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
11157
11477
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
11158
11478
|
}
|
|
11159
|
-
|
|
11479
|
+
writeFileSync5(outputPath, gif);
|
|
11160
11480
|
return null;
|
|
11161
11481
|
}
|
|
11162
11482
|
async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
@@ -11178,17 +11498,17 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11178
11498
|
try {
|
|
11179
11499
|
const run = { signal: input.signal, timeout: input.timeout };
|
|
11180
11500
|
for (const args of [buildGifPalettegenArgs(argsInput), buildGifPaletteuseArgs(argsInput)]) {
|
|
11181
|
-
const result = await
|
|
11501
|
+
const result = await runFfmpeg6(args, run);
|
|
11182
11502
|
if (!result.success) {
|
|
11183
11503
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
11184
11504
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11185
11505
|
}
|
|
11186
11506
|
}
|
|
11187
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
11507
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync8(outputPath)) !== false) {
|
|
11188
11508
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
11189
11509
|
if (failure) return failure;
|
|
11190
11510
|
}
|
|
11191
|
-
const fileSize = existsSync15(outputPath) ?
|
|
11511
|
+
const fileSize = existsSync15(outputPath) ? statSync7(outputPath).size : 0;
|
|
11192
11512
|
return {
|
|
11193
11513
|
success: true,
|
|
11194
11514
|
outputPath,
|
|
@@ -11197,7 +11517,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11197
11517
|
fileSize
|
|
11198
11518
|
};
|
|
11199
11519
|
} finally {
|
|
11200
|
-
|
|
11520
|
+
rmSync11(input.palettePath, { force: true });
|
|
11201
11521
|
}
|
|
11202
11522
|
}
|
|
11203
11523
|
function startEncodeProgress(job, stage, onProgress) {
|
|
@@ -11269,10 +11589,10 @@ function encodeWhileCapturing(input, totalFrames) {
|
|
|
11269
11589
|
const stream = await encoder;
|
|
11270
11590
|
for (let next = 0; next < totalFrames; next++) {
|
|
11271
11591
|
while (!landed.has(next) && !captureDone && !signal.aborted) {
|
|
11272
|
-
await new Promise((
|
|
11592
|
+
await new Promise((resolve11) => wake = resolve11);
|
|
11273
11593
|
}
|
|
11274
11594
|
if (signal.aborted) return;
|
|
11275
|
-
const mergedPath =
|
|
11595
|
+
const mergedPath = join26(input.framesDir, formatCaptureFrameName(next, "jpg"));
|
|
11276
11596
|
const frame = await readLandedFrame(landed.get(next), mergedPath);
|
|
11277
11597
|
landed.delete(next);
|
|
11278
11598
|
if (!await stream.writeFrame(frame)) {
|
|
@@ -11347,7 +11667,7 @@ async function runEncodeStage(input) {
|
|
|
11347
11667
|
const stage5Start = Date.now();
|
|
11348
11668
|
if (isPngSequence) {
|
|
11349
11669
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
11350
|
-
if (!existsSync15(outputPath))
|
|
11670
|
+
if (!existsSync15(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
11351
11671
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
11352
11672
|
if (captured.length === 0) {
|
|
11353
11673
|
throw new Error(
|
|
@@ -11355,11 +11675,11 @@ async function runEncodeStage(input) {
|
|
|
11355
11675
|
);
|
|
11356
11676
|
}
|
|
11357
11677
|
captured.forEach((name, i) => {
|
|
11358
|
-
const dst =
|
|
11359
|
-
|
|
11678
|
+
const dst = join26(outputPath, formatExportFrameName(i, "png"));
|
|
11679
|
+
copyFileSync4(join26(framesDir, name), dst);
|
|
11360
11680
|
});
|
|
11361
11681
|
if (hasAudio && audioOutputPath && existsSync15(audioOutputPath)) {
|
|
11362
|
-
|
|
11682
|
+
copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
|
|
11363
11683
|
log.info(
|
|
11364
11684
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
11365
11685
|
);
|
|
@@ -11379,7 +11699,7 @@ async function runEncodeStage(input) {
|
|
|
11379
11699
|
const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
|
|
11380
11700
|
fps: job.config.fps,
|
|
11381
11701
|
loop,
|
|
11382
|
-
palettePath:
|
|
11702
|
+
palettePath: join26(dirname10(videoOnlyPath), "gif-palette.png"),
|
|
11383
11703
|
preserveAlpha: needsAlpha,
|
|
11384
11704
|
signal: abortSignal,
|
|
11385
11705
|
timeout: engineCfg.ffmpegEncodeTimeout
|
|
@@ -11589,9 +11909,9 @@ function sampleDirectoryBytes(dir) {
|
|
|
11589
11909
|
continue;
|
|
11590
11910
|
}
|
|
11591
11911
|
for (const name of entries) {
|
|
11592
|
-
const full =
|
|
11912
|
+
const full = join27(current, name);
|
|
11593
11913
|
try {
|
|
11594
|
-
const st =
|
|
11914
|
+
const st = statSync8(full);
|
|
11595
11915
|
if (st.isDirectory()) {
|
|
11596
11916
|
stack.push(full);
|
|
11597
11917
|
} else if (st.isFile()) {
|
|
@@ -11738,8 +12058,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
11738
12058
|
const ranges = [];
|
|
11739
12059
|
let rangeStart = null;
|
|
11740
12060
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
11741
|
-
const framePath =
|
|
11742
|
-
const missing = !existsSync16(framePath) ||
|
|
12061
|
+
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12062
|
+
const missing = !existsSync16(framePath) || statSync8(framePath).size <= 8;
|
|
11743
12063
|
if (missing && rangeStart === null) {
|
|
11744
12064
|
rangeStart = frameIndex;
|
|
11745
12065
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -11755,7 +12075,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
11755
12075
|
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
|
|
11756
12076
|
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
11757
12077
|
const batches = [];
|
|
11758
|
-
const outputDirFor = (batchIndex, workerId) =>
|
|
12078
|
+
const outputDirFor = (batchIndex, workerId) => join27(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
|
|
11759
12079
|
if (stride > 1) {
|
|
11760
12080
|
const spans = /* @__PURE__ */ new Map();
|
|
11761
12081
|
for (const range of ranges) {
|
|
@@ -11815,8 +12135,8 @@ function getNextRetryWorkerCount(currentWorkers) {
|
|
|
11815
12135
|
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
11816
12136
|
}
|
|
11817
12137
|
function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
|
|
11818
|
-
if (platform2 === "win32" || platform2 === "darwin") return
|
|
11819
|
-
return
|
|
12138
|
+
if (platform2 === "win32" || platform2 === "darwin") return join27(systemTempDir, "hf-render-");
|
|
12139
|
+
return join27(dirname11(outputPath), `work-${jobId}-`);
|
|
11820
12140
|
}
|
|
11821
12141
|
var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
|
|
11822
12142
|
function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
|
|
@@ -11858,7 +12178,7 @@ The composition is too large for the available memory. To reduce memory pressure
|
|
|
11858
12178
|
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
11859
12179
|
let captured = 0;
|
|
11860
12180
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
11861
|
-
const framePath =
|
|
12181
|
+
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
11862
12182
|
if (existsSync16(framePath)) captured++;
|
|
11863
12183
|
}
|
|
11864
12184
|
return captured;
|
|
@@ -11884,7 +12204,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11884
12204
|
reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
|
|
11885
12205
|
});
|
|
11886
12206
|
pendingTransientRetry = false;
|
|
11887
|
-
const attemptWorkDir =
|
|
12207
|
+
const attemptWorkDir = join27(options.workDir, `capture-attempt-${attempt}`);
|
|
11888
12208
|
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
11889
12209
|
missingRanges,
|
|
11890
12210
|
currentWorkers,
|
|
@@ -12490,18 +12810,18 @@ function deVerifyFallbackTelemetry(err) {
|
|
|
12490
12810
|
};
|
|
12491
12811
|
}
|
|
12492
12812
|
function resolveRenderDebugDir() {
|
|
12493
|
-
const producerRoot = process.env.PRODUCER_RENDERS_DIR ?
|
|
12494
|
-
return
|
|
12813
|
+
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve8(process.env.PRODUCER_RENDERS_DIR, "..") : resolve8(dirname11(fileURLToPath4(import.meta.url)), "../..");
|
|
12814
|
+
return join27(producerRoot, ".debug");
|
|
12495
12815
|
}
|
|
12496
12816
|
async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
|
|
12497
12817
|
validateHlsRenderConfig(job.config);
|
|
12498
12818
|
const debugDir = resolveRenderDebugDir();
|
|
12499
|
-
const outputDir =
|
|
12500
|
-
if (!existsSync16(outputDir))
|
|
12501
|
-
const workDir = job.config.debug ?
|
|
12819
|
+
const outputDir = dirname11(outputPath);
|
|
12820
|
+
if (!existsSync16(outputDir)) mkdirSync10(outputDir, { recursive: true });
|
|
12821
|
+
const workDir = job.config.debug ? join27(debugDir, job.id) : createRenderWorkDir(resolveRenderWorkDirPrefix(outputPath, job.id), outputDir);
|
|
12502
12822
|
const pipelineStart = Date.now();
|
|
12503
12823
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
12504
|
-
const logPath = job.config.debug ?
|
|
12824
|
+
const logPath = job.config.debug ? join27(workDir, "render.log") : null;
|
|
12505
12825
|
const execution = new RenderExecutionContext({
|
|
12506
12826
|
request: { renderJobId: job.id, projectDir, outputPath },
|
|
12507
12827
|
logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
|
|
@@ -12515,7 +12835,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
12515
12835
|
log.info("KEEP_TEMP=1, leaving workDir on disk for inspection", { workDir });
|
|
12516
12836
|
return;
|
|
12517
12837
|
}
|
|
12518
|
-
|
|
12838
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
12519
12839
|
});
|
|
12520
12840
|
try {
|
|
12521
12841
|
await executeRenderPipeline({
|
|
@@ -12561,7 +12881,7 @@ async function executeRenderPipeline(input) {
|
|
|
12561
12881
|
imageDecodeFailures: 0
|
|
12562
12882
|
};
|
|
12563
12883
|
let hdrPerf;
|
|
12564
|
-
const perfOutputPath =
|
|
12884
|
+
const perfOutputPath = join27(workDir, "perf-summary.json");
|
|
12565
12885
|
const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
|
|
12566
12886
|
const observability = new RenderObservabilityRecorder({
|
|
12567
12887
|
pipelineStartMs: pipelineStart,
|
|
@@ -12634,7 +12954,7 @@ async function executeRenderPipeline(input) {
|
|
|
12634
12954
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
12635
12955
|
assertNotAborted();
|
|
12636
12956
|
assertConfiguredFfmpegBinariesExist();
|
|
12637
|
-
if (!existsSync16(workDir))
|
|
12957
|
+
if (!existsSync16(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
12638
12958
|
if (job.config.debug) {
|
|
12639
12959
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
12640
12960
|
}
|
|
@@ -12663,22 +12983,22 @@ async function executeRenderPipeline(input) {
|
|
|
12663
12983
|
requestedWorkers: job.config.workers ?? "auto"
|
|
12664
12984
|
});
|
|
12665
12985
|
const entryFile = job.config.entryFile || "index.html";
|
|
12666
|
-
let htmlPath =
|
|
12986
|
+
let htmlPath = join27(projectDir, entryFile);
|
|
12667
12987
|
if (!existsSync16(htmlPath)) {
|
|
12668
12988
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
12669
12989
|
}
|
|
12670
12990
|
assertNotAborted();
|
|
12671
|
-
const rawEntry =
|
|
12991
|
+
const rawEntry = readFileSync9(htmlPath, "utf-8");
|
|
12672
12992
|
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
12673
|
-
const wrapperPath =
|
|
12674
|
-
const projectIndexPath =
|
|
12993
|
+
const wrapperPath = join27(workDir, "standalone-entry.html");
|
|
12994
|
+
const projectIndexPath = join27(projectDir, "index.html");
|
|
12675
12995
|
if (!existsSync16(projectIndexPath)) {
|
|
12676
12996
|
throw new Error(
|
|
12677
12997
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
12678
12998
|
);
|
|
12679
12999
|
}
|
|
12680
13000
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
12681
|
-
|
|
13001
|
+
readFileSync9(projectIndexPath, "utf-8"),
|
|
12682
13002
|
entryFile,
|
|
12683
13003
|
rawEntry
|
|
12684
13004
|
);
|
|
@@ -12687,7 +13007,7 @@ async function executeRenderPipeline(input) {
|
|
|
12687
13007
|
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
12688
13008
|
);
|
|
12689
13009
|
}
|
|
12690
|
-
|
|
13010
|
+
writeFileSync6(wrapperPath, standaloneHtml, "utf-8");
|
|
12691
13011
|
htmlPath = wrapperPath;
|
|
12692
13012
|
log.info("Extracted standalone entry from index.html host context", {
|
|
12693
13013
|
entryFile
|
|
@@ -12834,7 +13154,7 @@ async function executeRenderPipeline(input) {
|
|
|
12834
13154
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
|
|
12835
13155
|
code: "extract_video"
|
|
12836
13156
|
});
|
|
12837
|
-
const compiledDir =
|
|
13157
|
+
const compiledDir = join27(workDir, "compiled");
|
|
12838
13158
|
const extractResult = await observeRenderStage(
|
|
12839
13159
|
observability,
|
|
12840
13160
|
"video_extract",
|
|
@@ -12968,7 +13288,7 @@ async function executeRenderPipeline(input) {
|
|
|
12968
13288
|
try {
|
|
12969
13289
|
fileServer = await createFileServer({
|
|
12970
13290
|
projectDir,
|
|
12971
|
-
compiledDir:
|
|
13291
|
+
compiledDir: join27(workDir, "compiled"),
|
|
12972
13292
|
port: 0,
|
|
12973
13293
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
12974
13294
|
fps: job.config.fps
|
|
@@ -12986,8 +13306,8 @@ async function executeRenderPipeline(input) {
|
|
|
12986
13306
|
if (!activeFileServer) {
|
|
12987
13307
|
throw new Error("File server failed to initialize before frame capture");
|
|
12988
13308
|
}
|
|
12989
|
-
const framesDir =
|
|
12990
|
-
if (!existsSync16(framesDir))
|
|
13309
|
+
const framesDir = join27(workDir, "captured-frames");
|
|
13310
|
+
if (!existsSync16(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
12991
13311
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
12992
13312
|
code: "start_browsers"
|
|
12993
13313
|
});
|
|
@@ -13458,7 +13778,7 @@ async function executeRenderPipeline(input) {
|
|
|
13458
13778
|
hls: ".mp4"
|
|
13459
13779
|
};
|
|
13460
13780
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
13461
|
-
const videoOnlyPath =
|
|
13781
|
+
const videoOnlyPath = join27(workDir, `video-only${videoExt}`);
|
|
13462
13782
|
const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
|
|
13463
13783
|
if (usePageSideCompositingForTransitions) {
|
|
13464
13784
|
activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
|
|
@@ -13711,7 +14031,7 @@ async function executeRenderPipeline(input) {
|
|
|
13711
14031
|
pageMotionBlur: job.config.pageMotionBlur,
|
|
13712
14032
|
captureQuality: captureOptions.quality
|
|
13713
14033
|
});
|
|
13714
|
-
const segmentDir = segmentDirFor(
|
|
14034
|
+
const segmentDir = segmentDirFor(join27(projectDir, "renders"), segmentPlanHash);
|
|
13715
14035
|
let completedSegments = /* @__PURE__ */ new Set();
|
|
13716
14036
|
if (job.config.resumeSegments === true) {
|
|
13717
14037
|
const existing = readSegmentManifest(segmentDir);
|
|
@@ -13726,10 +14046,10 @@ async function executeRenderPipeline(input) {
|
|
|
13726
14046
|
});
|
|
13727
14047
|
}
|
|
13728
14048
|
if (completedSegments.size === 0) {
|
|
13729
|
-
|
|
14049
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13730
14050
|
}
|
|
13731
14051
|
} else {
|
|
13732
|
-
|
|
14052
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13733
14053
|
}
|
|
13734
14054
|
const segmentManifest = readSegmentManifest(segmentDir) ?? {
|
|
13735
14055
|
version: 1,
|
|
@@ -13810,7 +14130,7 @@ async function executeRenderPipeline(input) {
|
|
|
13810
14130
|
);
|
|
13811
14131
|
updateCaptureObservability({ segmentRetries: segmentedRes.segmentRetries });
|
|
13812
14132
|
if (job.config.keepSegments !== true) {
|
|
13813
|
-
|
|
14133
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13814
14134
|
}
|
|
13815
14135
|
} else {
|
|
13816
14136
|
capturePlan = replanAfterFailure(capturePlan, { kind: "streaming_unavailable" });
|
|
@@ -14082,8 +14402,8 @@ async function executeRenderPipeline(input) {
|
|
|
14082
14402
|
"capture_disk",
|
|
14083
14403
|
"drawElement capture failed; retrying with a fresh screenshot session"
|
|
14084
14404
|
);
|
|
14085
|
-
|
|
14086
|
-
|
|
14405
|
+
rmSync12(framesDir, { recursive: true, force: true });
|
|
14406
|
+
mkdirSync10(framesDir, { recursive: true });
|
|
14087
14407
|
resetCaptureAttemptProgress(job);
|
|
14088
14408
|
dedupPerfs.length = 0;
|
|
14089
14409
|
cfg.useDrawElement = false;
|
|
@@ -14259,7 +14579,7 @@ async function executeRenderPipeline(input) {
|
|
|
14259
14579
|
job.perfSummary = perfSummary;
|
|
14260
14580
|
if (job.config.debug) {
|
|
14261
14581
|
try {
|
|
14262
|
-
|
|
14582
|
+
writeFileSync6(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
|
|
14263
14583
|
} catch (err) {
|
|
14264
14584
|
log.debug("Failed to write perf summary", {
|
|
14265
14585
|
perfOutputPath,
|
|
@@ -14269,8 +14589,8 @@ async function executeRenderPipeline(input) {
|
|
|
14269
14589
|
}
|
|
14270
14590
|
if (job.config.debug) {
|
|
14271
14591
|
if (!isPngSequence && !isHls && existsSync16(stagedOutputPath)) {
|
|
14272
|
-
const debugOutput =
|
|
14273
|
-
|
|
14592
|
+
const debugOutput = join27(workDir, `output${videoExt}`);
|
|
14593
|
+
copyFileSync5(stagedOutputPath, debugOutput);
|
|
14274
14594
|
}
|
|
14275
14595
|
}
|
|
14276
14596
|
await commitArtifactTransaction(artifactTransaction, () => {
|
|
@@ -14375,14 +14695,14 @@ async function executeRenderPipeline(input) {
|
|
|
14375
14695
|
// src/services/hyperframeLint.ts
|
|
14376
14696
|
import {
|
|
14377
14697
|
existsSync as existsSync17,
|
|
14378
|
-
readFileSync as
|
|
14379
|
-
statSync as
|
|
14380
|
-
openSync as
|
|
14698
|
+
readFileSync as readFileSync10,
|
|
14699
|
+
statSync as statSync9,
|
|
14700
|
+
openSync as openSync5,
|
|
14381
14701
|
fstatSync as fstatSync3,
|
|
14382
|
-
closeSync as
|
|
14702
|
+
closeSync as closeSync5,
|
|
14383
14703
|
constants as constants3
|
|
14384
14704
|
} from "node:fs";
|
|
14385
|
-
import { resolve as
|
|
14705
|
+
import { resolve as resolve9, join as join28, relative as relative3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
14386
14706
|
import { lintHyperframeHtml } from "@hyperframes/lint";
|
|
14387
14707
|
function isStringRecord(value) {
|
|
14388
14708
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -14412,30 +14732,30 @@ function pickEntryFile(files, preferredEntryFile) {
|
|
|
14412
14732
|
function readEntryHtml(filePath) {
|
|
14413
14733
|
let fd;
|
|
14414
14734
|
try {
|
|
14415
|
-
fd =
|
|
14735
|
+
fd = openSync5(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
|
|
14416
14736
|
} catch (error) {
|
|
14417
14737
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
|
|
14418
14738
|
return null;
|
|
14419
|
-
if (!
|
|
14739
|
+
if (!statSync9(filePath, { throwIfNoEntry: false })?.isFile()) return null;
|
|
14420
14740
|
throw error;
|
|
14421
14741
|
}
|
|
14422
14742
|
try {
|
|
14423
14743
|
if (!fstatSync3(fd).isFile()) return null;
|
|
14424
|
-
return
|
|
14744
|
+
return readFileSync10(fd, "utf-8");
|
|
14425
14745
|
} finally {
|
|
14426
|
-
|
|
14746
|
+
closeSync5(fd);
|
|
14427
14747
|
}
|
|
14428
14748
|
}
|
|
14429
14749
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
14430
|
-
const absProjectDir =
|
|
14431
|
-
if (!existsSync17(absProjectDir) || !
|
|
14750
|
+
const absProjectDir = resolve9(projectDir);
|
|
14751
|
+
if (!existsSync17(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
|
|
14432
14752
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
14433
14753
|
}
|
|
14434
14754
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
14435
14755
|
(value) => typeof value === "string" && value.trim().length > 0
|
|
14436
14756
|
);
|
|
14437
14757
|
for (const entryFile of entryCandidates) {
|
|
14438
|
-
const absoluteEntryPath =
|
|
14758
|
+
const absoluteEntryPath = resolve9(absProjectDir, entryFile);
|
|
14439
14759
|
const entryRelativePath = relative3(absProjectDir, absoluteEntryPath);
|
|
14440
14760
|
if (entryRelativePath === ".." || entryRelativePath.startsWith(`..${sep2}`) || isAbsolute3(entryRelativePath)) {
|
|
14441
14761
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
@@ -14450,7 +14770,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
14450
14770
|
}
|
|
14451
14771
|
}
|
|
14452
14772
|
return {
|
|
14453
|
-
error: `No HTML entry file found in project directory: ${
|
|
14773
|
+
error: `No HTML entry file found in project directory: ${join28(absProjectDir, preferredEntryFile || "index.html")}`
|
|
14454
14774
|
};
|
|
14455
14775
|
}
|
|
14456
14776
|
function prepareHyperframeLintBody(body) {
|
|
@@ -14496,7 +14816,7 @@ async function runHyperframeLint(prepared) {
|
|
|
14496
14816
|
// src/services/healthWorker.ts
|
|
14497
14817
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
14498
14818
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
14499
|
-
import { dirname as
|
|
14819
|
+
import { dirname as dirname12, join as join29 } from "node:path";
|
|
14500
14820
|
import { existsSync as existsSync18 } from "node:fs";
|
|
14501
14821
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
14502
14822
|
async function startHealthWorker(options = {}) {
|
|
@@ -14515,12 +14835,12 @@ async function startHealthWorker(options = {}) {
|
|
|
14515
14835
|
stdout: false,
|
|
14516
14836
|
stderr: false
|
|
14517
14837
|
});
|
|
14518
|
-
await new Promise((
|
|
14838
|
+
await new Promise((resolve11, reject) => {
|
|
14519
14839
|
const onMessage = (msg) => {
|
|
14520
14840
|
if (msg?.type === "listening") {
|
|
14521
14841
|
worker.off("error", onError);
|
|
14522
14842
|
worker.off("message", onMessage);
|
|
14523
|
-
|
|
14843
|
+
resolve11();
|
|
14524
14844
|
} else if (msg?.type === "listen-error") {
|
|
14525
14845
|
worker.off("error", onError);
|
|
14526
14846
|
worker.off("message", onMessage);
|
|
@@ -14571,8 +14891,8 @@ function defaultLogger2() {
|
|
|
14571
14891
|
};
|
|
14572
14892
|
}
|
|
14573
14893
|
function resolveWorkerEntry2() {
|
|
14574
|
-
const here =
|
|
14575
|
-
const candidates = [
|
|
14894
|
+
const here = dirname12(fileURLToPath5(import.meta.url));
|
|
14895
|
+
const candidates = [join29(here, "healthWorkerThread.js"), join29(here, "healthWorkerThread.ts")];
|
|
14576
14896
|
for (const candidate of candidates) {
|
|
14577
14897
|
if (existsSync18(candidate)) return candidate;
|
|
14578
14898
|
}
|
|
@@ -14608,7 +14928,7 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
|
14608
14928
|
|
|
14609
14929
|
// src/services/distributed/plan.ts
|
|
14610
14930
|
import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
|
|
14611
|
-
import { extname as extname4, isAbsolute as isAbsolute4, join as
|
|
14931
|
+
import { extname as extname4, isAbsolute as isAbsolute4, join as join30, relative as relative4, sep as sep3 } from "node:path";
|
|
14612
14932
|
import {
|
|
14613
14933
|
fpsToNumber as fpsToNumber10,
|
|
14614
14934
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
@@ -14618,7 +14938,7 @@ import {
|
|
|
14618
14938
|
normalizeVp9CpuUsed,
|
|
14619
14939
|
resolveConfig as resolveConfig3,
|
|
14620
14940
|
declareContentAddressedUrl,
|
|
14621
|
-
renameWithRetry as
|
|
14941
|
+
renameWithRetry as renameWithRetry7,
|
|
14622
14942
|
declareProbeFactsUrl
|
|
14623
14943
|
} from "@hyperframes/engine";
|
|
14624
14944
|
|
|
@@ -14689,10 +15009,10 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
14689
15009
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
14690
15010
|
|
|
14691
15011
|
// src/services/distributed/chunkAudio.ts
|
|
14692
|
-
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as
|
|
15012
|
+
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
14693
15013
|
|
|
14694
15014
|
// src/services/distributed/mp4Stitch.ts
|
|
14695
|
-
import { renameWithRetry as
|
|
15015
|
+
import { renameWithRetry as renameWithRetry6 } from "@hyperframes/engine";
|
|
14696
15016
|
var SCAN_BYTES = 64 * 1024;
|
|
14697
15017
|
|
|
14698
15018
|
// src/services/distributed/chunkAudio.ts
|
|
@@ -14712,9 +15032,9 @@ var PLAN_ROOT_CATEGORY = {
|
|
|
14712
15032
|
var PLAN_DIR_SIZE_LIMIT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
14713
15033
|
var FREEZE_OWNED_PLAN_FILES = [
|
|
14714
15034
|
"plan.json",
|
|
14715
|
-
|
|
14716
|
-
|
|
14717
|
-
|
|
15035
|
+
join30("meta", "composition.json"),
|
|
15036
|
+
join30("meta", "encoder.json"),
|
|
15037
|
+
join30("meta", "chunks.json")
|
|
14718
15038
|
];
|
|
14719
15039
|
|
|
14720
15040
|
// src/services/distributed/renderConfigValidation.ts
|
|
@@ -14945,6 +15265,8 @@ var DEFAULT_SERVER_FPS = { num: 30, den: 1 };
|
|
|
14945
15265
|
var SSE_KEEPALIVE_MS = 3e4;
|
|
14946
15266
|
var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
14947
15267
|
"ASSET_MEDIA_TYPE_MISMATCH",
|
|
15268
|
+
"HEIF_IMAGE_CONVERSION_FAILED",
|
|
15269
|
+
"HEIF_TOOL_UNAVAILABLE",
|
|
14948
15270
|
"NOT_MEDIA_PAYLOAD",
|
|
14949
15271
|
"INVALID_VIDEO_METADATA",
|
|
14950
15272
|
"VIDEO_SOURCE_UNRENDERABLE",
|
|
@@ -15080,8 +15402,8 @@ function buildRenderJobConfig(input, outputPath, log) {
|
|
|
15080
15402
|
function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
15081
15403
|
const { input, cleanupProjectDir } = prepared;
|
|
15082
15404
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
15083
|
-
const outputDir =
|
|
15084
|
-
if (!existsSync19(outputDir))
|
|
15405
|
+
const outputDir = dirname13(absoluteOutputPath);
|
|
15406
|
+
if (!existsSync19(outputDir)) mkdirSync11(outputDir, { recursive: true });
|
|
15085
15407
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
15086
15408
|
}
|
|
15087
15409
|
function validateRenderOverrides(body) {
|
|
@@ -15121,12 +15443,12 @@ var MAX_PREVIEW_HTML_BYTES = 20 * 1024 * 1024;
|
|
|
15121
15443
|
function prepareProjectDirectory(projectDir, options) {
|
|
15122
15444
|
const candidate = nonEmptyString(projectDir);
|
|
15123
15445
|
if (!candidate) return null;
|
|
15124
|
-
const absProjectDir =
|
|
15125
|
-
if (!existsSync19(absProjectDir) || !
|
|
15446
|
+
const absProjectDir = resolve10(candidate);
|
|
15447
|
+
if (!existsSync19(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
|
|
15126
15448
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15127
15449
|
}
|
|
15128
15450
|
const entry = options.entryFile || "index.html";
|
|
15129
|
-
if (!existsSync19(
|
|
15451
|
+
if (!existsSync19(resolve10(absProjectDir, entry))) {
|
|
15130
15452
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
15131
15453
|
}
|
|
15132
15454
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -15147,8 +15469,8 @@ async function resolveInlineRenderHtml(body) {
|
|
|
15147
15469
|
}
|
|
15148
15470
|
function materializeInlineProject(html, options) {
|
|
15149
15471
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
|
|
15150
|
-
const tempProjectDir = mkdtempSync4(
|
|
15151
|
-
|
|
15472
|
+
const tempProjectDir = mkdtempSync4(join31(tempRoot, "producer-project-"));
|
|
15473
|
+
writeFileSync7(join31(tempProjectDir, "index.html"), html, "utf-8");
|
|
15152
15474
|
return {
|
|
15153
15475
|
prepared: {
|
|
15154
15476
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -15173,7 +15495,7 @@ function resolveOutputPath(projectDir, outputCandidate, rendersDir, log) {
|
|
|
15173
15495
|
try {
|
|
15174
15496
|
return resolveRenderPaths(projectDir, outputCandidate, rendersDir).absoluteOutputPath;
|
|
15175
15497
|
} catch (error) {
|
|
15176
|
-
const fallbackPath =
|
|
15498
|
+
const fallbackPath = resolve10(rendersDir, `producer-fallback-${Date.now()}.mp4`);
|
|
15177
15499
|
log.warn("Failed to resolve output path, using fallback", {
|
|
15178
15500
|
fallback: fallbackPath,
|
|
15179
15501
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -15209,7 +15531,7 @@ function createArtifactStore(ttlMs) {
|
|
|
15209
15531
|
function cleanupTempDir(dir, log) {
|
|
15210
15532
|
if (!dir) return;
|
|
15211
15533
|
try {
|
|
15212
|
-
|
|
15534
|
+
rmSync13(dir, { recursive: true, force: true });
|
|
15213
15535
|
} catch (error) {
|
|
15214
15536
|
log.warn("Failed to cleanup temp project dir", {
|
|
15215
15537
|
cleanupProjectDir: dir,
|
|
@@ -15218,7 +15540,7 @@ function cleanupTempDir(dir, log) {
|
|
|
15218
15540
|
}
|
|
15219
15541
|
}
|
|
15220
15542
|
function outputFileSize(path) {
|
|
15221
|
-
return existsSync19(path) ?
|
|
15543
|
+
return existsSync19(path) ? statSync10(path).size : 0;
|
|
15222
15544
|
}
|
|
15223
15545
|
function createBlockingProgressReporter(log, requestId) {
|
|
15224
15546
|
let lastLoggedPct = -10;
|
|
@@ -15524,7 +15846,7 @@ function createRenderHandlers(options = {}) {
|
|
|
15524
15846
|
store.delete(token);
|
|
15525
15847
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
15526
15848
|
}
|
|
15527
|
-
const stats =
|
|
15849
|
+
const stats = statSync10(artifact.path);
|
|
15528
15850
|
return new Response(createReadStream3(artifact.path), {
|
|
15529
15851
|
headers: {
|
|
15530
15852
|
"content-type": "video/mp4",
|
|
@@ -15591,7 +15913,7 @@ function startServer(options = {}) {
|
|
|
15591
15913
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
15592
15914
|
return server;
|
|
15593
15915
|
}
|
|
15594
|
-
var entryScript = process.argv[1] ?
|
|
15916
|
+
var entryScript = process.argv[1] ? resolve10(process.argv[1]) : "";
|
|
15595
15917
|
var isPublicServerEntry = entryScript.endsWith("/public-server.js") || entryScript.endsWith("/src/server.ts");
|
|
15596
15918
|
if (isPublicServerEntry) {
|
|
15597
15919
|
const { values } = parseArgs({
|