@hyperframes/producer 0.8.205 → 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 +745 -437
- 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 +818 -506
- package/dist/public-server.js +540 -230
- 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,
|
|
@@ -5867,7 +5881,7 @@ function readLoopOverride(el) {
|
|
|
5867
5881
|
}
|
|
5868
5882
|
return null;
|
|
5869
5883
|
}
|
|
5870
|
-
function
|
|
5884
|
+
function resolveLocalImageSourcePath(src, options) {
|
|
5871
5885
|
const trimmed = src.trim();
|
|
5872
5886
|
if (!trimmed || trimmed.startsWith("data:")) return null;
|
|
5873
5887
|
const { basePath } = splitUrlSuffix(trimmed);
|
|
@@ -6090,7 +6104,7 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6090
6104
|
const img = images[i];
|
|
6091
6105
|
const src = (img.getAttribute("src") || "").trim();
|
|
6092
6106
|
if (!hasGifExtension(src)) continue;
|
|
6093
|
-
const sourcePath =
|
|
6107
|
+
const sourcePath = resolveLocalImageSourcePath(src, options);
|
|
6094
6108
|
if (!sourcePath) continue;
|
|
6095
6109
|
const bytes = readFileSync5(sourcePath);
|
|
6096
6110
|
const metadata = parseAnimatedGifMetadata(bytes);
|
|
@@ -6138,19 +6152,74 @@ async function prepareAnimatedGifInputs(html, options) {
|
|
|
6138
6152
|
};
|
|
6139
6153
|
}
|
|
6140
6154
|
|
|
6141
|
-
// src/services/
|
|
6142
|
-
import {
|
|
6143
|
-
import { getPositionEditsRenderScript } from "@hyperframes/core/runtime/position-edits-render";
|
|
6144
|
-
|
|
6145
|
-
// src/services/assetMediaType.ts
|
|
6146
|
-
import { existsSync as existsSync9 } from "node:fs";
|
|
6155
|
+
// src/services/heifImagePrep.ts
|
|
6156
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
6147
6157
|
import {
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
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
|
+
}
|
|
6154
6223
|
|
|
6155
6224
|
// src/utils/semaphore.ts
|
|
6156
6225
|
var Semaphore = class {
|
|
@@ -6165,10 +6234,10 @@ var Semaphore = class {
|
|
|
6165
6234
|
this.active++;
|
|
6166
6235
|
return () => this.release();
|
|
6167
6236
|
}
|
|
6168
|
-
return new Promise((
|
|
6237
|
+
return new Promise((resolve11) => {
|
|
6169
6238
|
this.queue.push(() => {
|
|
6170
6239
|
this.active++;
|
|
6171
|
-
|
|
6240
|
+
resolve11(() => this.release());
|
|
6172
6241
|
});
|
|
6173
6242
|
});
|
|
6174
6243
|
}
|
|
@@ -6199,7 +6268,229 @@ async function withMediaProbeSlot(operation) {
|
|
|
6199
6268
|
}
|
|
6200
6269
|
}
|
|
6201
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
|
+
|
|
6202
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";
|
|
6203
6494
|
var ASSET_MEDIA_TYPE_MISMATCH = "ASSET_MEDIA_TYPE_MISMATCH";
|
|
6204
6495
|
var AssetMediaTypeMismatchError = class extends Error {
|
|
6205
6496
|
code = ASSET_MEDIA_TYPE_MISMATCH;
|
|
@@ -6309,7 +6600,7 @@ function logRemoteDownloadTelemetry(event) {
|
|
|
6309
6600
|
}
|
|
6310
6601
|
var INFERRED_MEDIA_DURATION_ATTR = "data-hf-inferred-duration";
|
|
6311
6602
|
function parseSubCompHtmlForValidity(html) {
|
|
6312
|
-
return
|
|
6603
|
+
return parseHTML5(html).document;
|
|
6313
6604
|
}
|
|
6314
6605
|
function endsWithSourceUrl(code) {
|
|
6315
6606
|
const trimmed = code.trimEnd();
|
|
@@ -6317,7 +6608,7 @@ function endsWithSourceUrl(code) {
|
|
|
6317
6608
|
return lastLine.startsWith("//# sourceURL=") || lastLine.startsWith("//@ sourceURL=");
|
|
6318
6609
|
}
|
|
6319
6610
|
function prepareCompositionScripts(html) {
|
|
6320
|
-
const { document: document2 } =
|
|
6611
|
+
const { document: document2 } = parseHTML5(html);
|
|
6321
6612
|
for (const el of document2.querySelectorAll("script:not([src])")) {
|
|
6322
6613
|
const isModule = (el.getAttribute("type") || "").trim().toLowerCase() === "module";
|
|
6323
6614
|
const runsAsScript = isModule || isJavaScriptType(el);
|
|
@@ -6354,14 +6645,14 @@ Check that each file referenced by data-composition-src contains valid HTML with
|
|
|
6354
6645
|
}
|
|
6355
6646
|
};
|
|
6356
6647
|
function assertSubCompositionsUsable(html, projectDir, visited = /* @__PURE__ */ new Set()) {
|
|
6357
|
-
const { document: document2 } =
|
|
6648
|
+
const { document: document2 } = parseHTML5(html);
|
|
6358
6649
|
const hosts = [...document2.querySelectorAll("[data-composition-src]")];
|
|
6359
6650
|
const problems = [];
|
|
6360
6651
|
for (const el of hosts) {
|
|
6361
6652
|
const srcPath = el.getAttribute("data-composition-src");
|
|
6362
6653
|
if (!srcPath) continue;
|
|
6363
6654
|
if (isUnresolvedAssetPlaceholder(srcPath)) continue;
|
|
6364
|
-
const filePath =
|
|
6655
|
+
const filePath = resolve7(projectDir, srcPath);
|
|
6365
6656
|
if (visited.has(filePath)) continue;
|
|
6366
6657
|
const read = readProjectFile(filePath);
|
|
6367
6658
|
if (read.kind === "missing") {
|
|
@@ -6417,7 +6708,7 @@ function runtimeRoot(document2) {
|
|
|
6417
6708
|
}
|
|
6418
6709
|
function detectRenderModeHints(html, pageMotionBlur) {
|
|
6419
6710
|
const reasons = [];
|
|
6420
|
-
const { document: document2 } =
|
|
6711
|
+
const { document: document2 } = parseHTML5(html);
|
|
6421
6712
|
if (document2.querySelector("canvas[layoutsubtree]")) {
|
|
6422
6713
|
reasons.push({
|
|
6423
6714
|
code: "htmlInCanvas",
|
|
@@ -6459,7 +6750,7 @@ function detectThreeDTransformUsage(html) {
|
|
|
6459
6750
|
}
|
|
6460
6751
|
var BACKGROUND_IMAGE_DECL_PATTERN = /(?:^|;|\{)\s*background(?:-image)?\s*:[^;}]*(?:\bgradient\s*\(|url\s*\()/i;
|
|
6461
6752
|
function detectAncestorBackgroundImage(html) {
|
|
6462
|
-
const { document: document2 } =
|
|
6753
|
+
const { document: document2 } = parseHTML5(html);
|
|
6463
6754
|
const root = document2.querySelector("[data-composition-id]");
|
|
6464
6755
|
if (!root) return false;
|
|
6465
6756
|
const ancestors = [];
|
|
@@ -6506,7 +6797,7 @@ function detectShaderTransitionUsage(html) {
|
|
|
6506
6797
|
async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, downloadDir, tagName, elementIdentity, log, urlByAbsentLocalPath) {
|
|
6507
6798
|
let filePath = src;
|
|
6508
6799
|
if (isHttpUrl(src)) {
|
|
6509
|
-
if (!existsSync10(downloadDir))
|
|
6800
|
+
if (!existsSync10(downloadDir)) mkdirSync7(downloadDir, { recursive: true });
|
|
6510
6801
|
try {
|
|
6511
6802
|
filePath = await downloadToTemp(src, downloadDir, void 0, void 0, void 0, {
|
|
6512
6803
|
onTelemetry: logRemoteDownloadTelemetry
|
|
@@ -6516,7 +6807,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6516
6807
|
}
|
|
6517
6808
|
} else {
|
|
6518
6809
|
filePath = decodedUrlPath(src);
|
|
6519
|
-
if (!filePath.startsWith("/")) filePath =
|
|
6810
|
+
if (!filePath.startsWith("/")) filePath = join15(baseDir, filePath);
|
|
6520
6811
|
filePath = urlByAbsentLocalPath?.get(filePath) ?? filePath;
|
|
6521
6812
|
}
|
|
6522
6813
|
if (!isHttpUrl(filePath) && !existsSync10(filePath)) {
|
|
@@ -6573,7 +6864,7 @@ async function resolveMediaDuration(src, mediaStart, playbackRate, baseDir, down
|
|
|
6573
6864
|
function markInferredVariableMediaDurations(html, unresolvedMedia) {
|
|
6574
6865
|
const unresolvedIds = new Set(unresolvedMedia.map((element) => element.id));
|
|
6575
6866
|
if (unresolvedIds.size === 0) return html;
|
|
6576
|
-
const { document: document2 } =
|
|
6867
|
+
const { document: document2 } = parseHTML5(html);
|
|
6577
6868
|
let changed = false;
|
|
6578
6869
|
for (const element of document2.querySelectorAll("video[data-var-src], audio[data-var-src]")) {
|
|
6579
6870
|
if (!unresolvedIds.has(element.id)) continue;
|
|
@@ -6644,13 +6935,13 @@ async function compileHtmlFile(html, baseDir, downloadDir, log, urlByAbsentLocal
|
|
|
6644
6935
|
}
|
|
6645
6936
|
async function parseSubCompositions(html, projectDir, downloadDir, visited = /* @__PURE__ */ new Set(), urlByAbsentLocalPath) {
|
|
6646
6937
|
const subCompositions = /* @__PURE__ */ new Map();
|
|
6647
|
-
const { document: document2 } =
|
|
6938
|
+
const { document: document2 } = parseHTML5(html);
|
|
6648
6939
|
const compEls = document2.querySelectorAll("[data-composition-src]");
|
|
6649
6940
|
const workItems = [];
|
|
6650
6941
|
for (const el of compEls) {
|
|
6651
6942
|
const srcPath = el.getAttribute("data-composition-src");
|
|
6652
6943
|
if (!srcPath) continue;
|
|
6653
|
-
const filePath =
|
|
6944
|
+
const filePath = resolve7(projectDir, srcPath);
|
|
6654
6945
|
if (visited.has(filePath)) {
|
|
6655
6946
|
continue;
|
|
6656
6947
|
}
|
|
@@ -6667,7 +6958,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6667
6958
|
workItems.map(async (item) => {
|
|
6668
6959
|
const { html: compiledSub } = await compileHtmlFile(
|
|
6669
6960
|
rewriteUnresolvableGsapToCdn(item.rawSubHtml, projectDir, item.srcPath),
|
|
6670
|
-
|
|
6961
|
+
dirname7(item.filePath),
|
|
6671
6962
|
downloadDir,
|
|
6672
6963
|
void 0,
|
|
6673
6964
|
urlByAbsentLocalPath
|
|
@@ -6695,7 +6986,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, visited = /*
|
|
|
6695
6986
|
return { subCompositions };
|
|
6696
6987
|
}
|
|
6697
6988
|
function promoteCssImportsToLinkTags(html) {
|
|
6698
|
-
const { document: document2 } =
|
|
6989
|
+
const { document: document2 } = parseHTML5(html);
|
|
6699
6990
|
const head = document2.querySelector("head");
|
|
6700
6991
|
if (!head) return html;
|
|
6701
6992
|
const importRe = /@import\s+url\(\s*['"]?([^'")\s]+)['"]?\s*\)\s*;?/gi;
|
|
@@ -6764,7 +7055,7 @@ var ProducerHostIdentityMap = class extends Map {
|
|
|
6764
7055
|
}
|
|
6765
7056
|
};
|
|
6766
7057
|
function coalesceHeadStylesAndBodyScripts(html) {
|
|
6767
|
-
const { document: document2 } =
|
|
7058
|
+
const { document: document2 } = parseHTML5(html);
|
|
6768
7059
|
const head = document2.querySelector("head");
|
|
6769
7060
|
const body = document2.querySelector("body");
|
|
6770
7061
|
if (!head && !body) return html;
|
|
@@ -6815,7 +7106,7 @@ function coalesceHeadStylesAndBodyScripts(html) {
|
|
|
6815
7106
|
return document2.toString();
|
|
6816
7107
|
}
|
|
6817
7108
|
function inlineSubCompositions(html, subCompositions, projectDir, variableOverrides = {}) {
|
|
6818
|
-
const { document: document2 } =
|
|
7109
|
+
const { document: document2 } = parseHTML5(html);
|
|
6819
7110
|
const head = document2.querySelector("head");
|
|
6820
7111
|
const body = document2.querySelector("body");
|
|
6821
7112
|
const hosts = Array.from(document2.querySelectorAll("[data-composition-src]"));
|
|
@@ -6844,17 +7135,17 @@ function inlineSubCompositions(html, subCompositions, projectDir, variableOverri
|
|
|
6844
7135
|
resolveHtml: (srcPath) => {
|
|
6845
7136
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
6846
7137
|
if (!compHtml) {
|
|
6847
|
-
const read = readProjectFile(
|
|
7138
|
+
const read = readProjectFile(resolve7(projectDir, srcPath));
|
|
6848
7139
|
if (read.kind === "file") {
|
|
6849
7140
|
compHtml = rewriteUnresolvableGsapToCdn(read.text, projectDir, srcPath);
|
|
6850
7141
|
}
|
|
6851
7142
|
}
|
|
6852
7143
|
return compHtml;
|
|
6853
7144
|
},
|
|
6854
|
-
parseHtml: (htmlStr) =>
|
|
7145
|
+
parseHtml: (htmlStr) => parseHTML5(htmlStr).document,
|
|
6855
7146
|
// Mirrors the preview bundler: a sub-composition's SIBLING assets resolve
|
|
6856
7147
|
// against its own directory, project-root refs stay as authored.
|
|
6857
|
-
assetExists: (path) => existsSync10(
|
|
7148
|
+
assetExists: (path) => existsSync10(resolve7(projectDir, path)),
|
|
6858
7149
|
// Preserve the authored root wrapper as a child of the host, matching
|
|
6859
7150
|
// the preview bundler's shape (htmlBundler.ts's prepareFlattenedInnerRoot,
|
|
6860
7151
|
// which the runtime compositionLoader mirrors with its own copy for the
|
|
@@ -6955,7 +7246,7 @@ ${html}
|
|
|
6955
7246
|
</html>`;
|
|
6956
7247
|
}
|
|
6957
7248
|
function injectTextRenderingRule(html) {
|
|
6958
|
-
const { document: document2 } =
|
|
7249
|
+
const { document: document2 } = parseHTML5(html);
|
|
6959
7250
|
const head = document2.querySelector("head");
|
|
6960
7251
|
if (!head) return html;
|
|
6961
7252
|
if (document2.querySelector("style[data-hyperframes-text-rendering]")) {
|
|
@@ -6979,7 +7270,7 @@ function matchesScriptIntegrity(bytes, metadata) {
|
|
|
6979
7270
|
(alg) => hashes.some((hash) => hash[1]?.toLowerCase() === alg)
|
|
6980
7271
|
);
|
|
6981
7272
|
if (!strongest) return true;
|
|
6982
|
-
const actual =
|
|
7273
|
+
const actual = createHash6(strongest).update(bytes).digest();
|
|
6983
7274
|
return hashes.some(
|
|
6984
7275
|
(hash) => hash[1]?.toLowerCase() === strongest && actual.equals(Buffer.from(hash[2], "base64"))
|
|
6985
7276
|
);
|
|
@@ -6987,7 +7278,7 @@ function matchesScriptIntegrity(bytes, metadata) {
|
|
|
6987
7278
|
async function inlineExternalScripts(html) {
|
|
6988
7279
|
const fullHtml = ensureFullDocument(html);
|
|
6989
7280
|
const wrappedFragment = fullHtml !== html;
|
|
6990
|
-
const { document: document2 } =
|
|
7281
|
+
const { document: document2 } = parseHTML5(fullHtml);
|
|
6991
7282
|
const scripts = document2.querySelectorAll("script[src]");
|
|
6992
7283
|
const externalScripts = [];
|
|
6993
7284
|
for (const el of scripts) {
|
|
@@ -7036,12 +7327,12 @@ ${safeText}
|
|
|
7036
7327
|
return wrappedFragment ? document2.body.innerHTML || "" : document2.toString();
|
|
7037
7328
|
}
|
|
7038
7329
|
function collectExternalAssets(html, projectDir) {
|
|
7039
|
-
const absProjectDir =
|
|
7330
|
+
const absProjectDir = resolve7(projectDir);
|
|
7040
7331
|
const externalAssets = /* @__PURE__ */ new Map();
|
|
7041
7332
|
function processPath(rawPath) {
|
|
7042
7333
|
const trimmed = rawPath.trim();
|
|
7043
7334
|
if (isNonRelativeUrl(trimmed)) return null;
|
|
7044
|
-
const absPath =
|
|
7335
|
+
const absPath = resolve7(absProjectDir, trimmed);
|
|
7045
7336
|
if (isPathInside2(absPath, absProjectDir)) {
|
|
7046
7337
|
return null;
|
|
7047
7338
|
}
|
|
@@ -7050,7 +7341,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
7050
7341
|
externalAssets.set(safeKey, absPath);
|
|
7051
7342
|
return safeKey;
|
|
7052
7343
|
}
|
|
7053
|
-
const { document: document2 } =
|
|
7344
|
+
const { document: document2 } = parseHTML5(html);
|
|
7054
7345
|
for (const el of document2.querySelectorAll("[src], [href]")) {
|
|
7055
7346
|
for (const attr of ["src", "href"]) {
|
|
7056
7347
|
const val = (el.getAttribute(attr) || "").trim();
|
|
@@ -7095,7 +7386,7 @@ var REMOTE_SOURCE_TAG_RE = /<source\b[^>]*?\bsrc\s*=\s*["'](https?:\/\/[^"']+)["
|
|
|
7095
7386
|
var REMOTE_IMG_TAG_RE = /<img\b[^>]*?(?<![\w-])src\s*=\s*["'](https?:\/\/[^"']+)["'][^>]*>/gi;
|
|
7096
7387
|
async function downloadAndRewriteUrls(urlSet, html, remoteDir, warnLabel, logLabel, extraRewrite) {
|
|
7097
7388
|
if (urlSet.size === 0) return { html, remoteMediaAssets: /* @__PURE__ */ new Map() };
|
|
7098
|
-
if (!existsSync10(remoteDir))
|
|
7389
|
+
if (!existsSync10(remoteDir)) mkdirSync7(remoteDir, { recursive: true });
|
|
7099
7390
|
const urlToLocal = /* @__PURE__ */ new Map();
|
|
7100
7391
|
await Promise.all(
|
|
7101
7392
|
[...urlSet].map(async (url) => {
|
|
@@ -7142,7 +7433,7 @@ async function localizeRemoteMediaSources(html, downloadDir) {
|
|
|
7142
7433
|
return downloadAndRewriteUrls(
|
|
7143
7434
|
urlSet,
|
|
7144
7435
|
html,
|
|
7145
|
-
|
|
7436
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7146
7437
|
"Remote media download failed",
|
|
7147
7438
|
"Localized remote media source(s)"
|
|
7148
7439
|
);
|
|
@@ -7157,7 +7448,7 @@ async function localizeRemoteImageSources(html, downloadDir) {
|
|
|
7157
7448
|
return downloadAndRewriteUrls(
|
|
7158
7449
|
urlSet,
|
|
7159
7450
|
html,
|
|
7160
|
-
|
|
7451
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7161
7452
|
"Remote image download failed",
|
|
7162
7453
|
"Localized remote image source(s)"
|
|
7163
7454
|
);
|
|
@@ -7173,7 +7464,7 @@ async function localizeRemoteBackgroundImages(html, downloadDir) {
|
|
|
7173
7464
|
return downloadAndRewriteUrls(
|
|
7174
7465
|
urlSet,
|
|
7175
7466
|
html,
|
|
7176
|
-
|
|
7467
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7177
7468
|
"Remote background-image download failed",
|
|
7178
7469
|
"Localized remote background-image(s)",
|
|
7179
7470
|
// Quoted url('..')/url("..") are rewritten by downloadAndRewriteUrls' default
|
|
@@ -7308,7 +7599,7 @@ async function localizeRemoteFontFaces(html, downloadDir) {
|
|
|
7308
7599
|
return downloadAndRewriteUrls(
|
|
7309
7600
|
urlSet,
|
|
7310
7601
|
processed,
|
|
7311
|
-
|
|
7602
|
+
join15(downloadDir, REMOTE_MEDIA_SUBDIR),
|
|
7312
7603
|
"Remote font download failed",
|
|
7313
7604
|
"Localized remote font face(s)",
|
|
7314
7605
|
(h, url, relPath) => h.replaceAll(`url(${url})`, `url("${relPath}")`)
|
|
@@ -7353,7 +7644,7 @@ async function embedLocalFontFaces(html, projectDir) {
|
|
|
7353
7644
|
while ((urlMatch = urlRe.exec(block)) !== null) {
|
|
7354
7645
|
const localPath = urlMatch[1];
|
|
7355
7646
|
if (!localPath || embeddedPaths.has(localPath)) continue;
|
|
7356
|
-
const absPath = localPath.startsWith("/") ? localPath :
|
|
7647
|
+
const absPath = localPath.startsWith("/") ? localPath : resolve7(projectDir, localPath);
|
|
7357
7648
|
if (!isPathInside2(absPath, projectDir)) continue;
|
|
7358
7649
|
const ext = absPath.match(/\.(woff2?|ttf|otf|ttc)$/i)?.[1]?.toLowerCase() ?? "ttf";
|
|
7359
7650
|
try {
|
|
@@ -7402,7 +7693,7 @@ function renderGsapDist() {
|
|
|
7402
7693
|
}
|
|
7403
7694
|
function rewriteUnresolvableGsapToCdn(html, projectDir, compSrcPath) {
|
|
7404
7695
|
let gsapDist;
|
|
7405
|
-
const exists = (path) => existsSync10(
|
|
7696
|
+
const exists = (path) => existsSync10(resolve7(projectDir, path));
|
|
7406
7697
|
return html.replace(
|
|
7407
7698
|
/(<script\b[^>]*\bsrc=["'])([^"']*gsap[^"']*\/dist\/([^"']+))(["'][^>]*>)/gi,
|
|
7408
7699
|
(full, prefix, src, file, suffix) => {
|
|
@@ -7418,8 +7709,8 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
7418
7709
|
if (!isPathInside2(htmlPath, projectDir)) return html;
|
|
7419
7710
|
const entryPath = relative2(projectDir, htmlPath).replace(/\\/g, "/");
|
|
7420
7711
|
if (!entryPath.includes("/")) return html;
|
|
7421
|
-
const { document: document2 } =
|
|
7422
|
-
const assetExists = (path) => existsSync10(
|
|
7712
|
+
const { document: document2 } = parseHTML5(html);
|
|
7713
|
+
const assetExists = (path) => existsSync10(resolve7(projectDir, path));
|
|
7423
7714
|
rewriteAssetPaths(
|
|
7424
7715
|
document2.querySelectorAll("[src], [href]"),
|
|
7425
7716
|
entryPath,
|
|
@@ -7441,7 +7732,7 @@ function rebaseDirectEntryAssetPaths(html, projectDir, htmlPath) {
|
|
|
7441
7732
|
}
|
|
7442
7733
|
async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
|
|
7443
7734
|
const entryHtml = rebaseDirectEntryAssetPaths(
|
|
7444
|
-
|
|
7735
|
+
readFileSync7(htmlPath, "utf-8"),
|
|
7445
7736
|
projectDir,
|
|
7446
7737
|
htmlPath
|
|
7447
7738
|
);
|
|
@@ -7511,12 +7802,26 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7511
7802
|
const { html: htmlWithLocalImages, remoteMediaAssets: remoteImageAssets } = await localizeRemoteImageSources(htmlWithLocalMedia, downloadDir);
|
|
7512
7803
|
const { html: htmlWithLocalBg, remoteMediaAssets: remoteBgAssets } = await localizeRemoteBackgroundImages(htmlWithLocalImages, downloadDir);
|
|
7513
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
|
+
}
|
|
7514
7819
|
const gifSourceAssets = new Map(remoteImageAssets);
|
|
7515
7820
|
const {
|
|
7516
7821
|
html: htmlWithPreparedGifs,
|
|
7517
7822
|
preparedAssets: preparedGifAssets,
|
|
7518
7823
|
preparedGifs
|
|
7519
|
-
} = await prepareAnimatedGifInputs(
|
|
7824
|
+
} = await prepareAnimatedGifInputs(htmlWithJpegs, {
|
|
7520
7825
|
projectDir,
|
|
7521
7826
|
downloadDir,
|
|
7522
7827
|
cacheDir: options.animatedGifCacheDir,
|
|
@@ -7543,11 +7848,14 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7543
7848
|
for (const [relPath, absPath] of preparedGifAssets) {
|
|
7544
7849
|
externalAssets.set(relPath, absPath);
|
|
7545
7850
|
}
|
|
7851
|
+
for (const [relPath, absPath] of preparedHeifAssets) {
|
|
7852
|
+
externalAssets.set(relPath, absPath);
|
|
7853
|
+
}
|
|
7546
7854
|
const { videos, audios, images, droppedMediaIds } = collectRenderMedia(html);
|
|
7547
7855
|
const masterFxChain = parseMasterFxChain(html);
|
|
7548
7856
|
for (const video of videos) {
|
|
7549
7857
|
if (isHttpUrl(video.src)) continue;
|
|
7550
|
-
const videoPath =
|
|
7858
|
+
const videoPath = resolve7(projectDir, video.src);
|
|
7551
7859
|
const reencode = `ffmpeg -i "${video.src}" -c:v libx264 -r 30 -g 30 -keyint_min 30 -movflags +faststart -c:a copy output.mp4`;
|
|
7552
7860
|
(async () => {
|
|
7553
7861
|
const metadata = await withMediaProbeSlot(() => extractMediaMetadata(videoPath));
|
|
@@ -7567,7 +7875,7 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
7567
7875
|
})().catch(() => {
|
|
7568
7876
|
});
|
|
7569
7877
|
}
|
|
7570
|
-
const { document: document2 } =
|
|
7878
|
+
const { document: document2 } = parseHTML5(html);
|
|
7571
7879
|
const rootEl = document2.querySelector("[data-composition-id]");
|
|
7572
7880
|
const width = rootEl ? parseInt(rootEl.getAttribute("data-width") || "1080", 10) : 1080;
|
|
7573
7881
|
const height = rootEl ? parseInt(rootEl.getAttribute("data-height") || "1920", 10) : 1920;
|
|
@@ -7974,7 +8282,7 @@ async function runCompileStage(input) {
|
|
|
7974
8282
|
abortSignal
|
|
7975
8283
|
} = input;
|
|
7976
8284
|
const compileStart = Date.now();
|
|
7977
|
-
const compiled = await compileForRender(projectDir, htmlPath,
|
|
8285
|
+
const compiled = await compileForRender(projectDir, htmlPath, join16(workDir, "downloads"), {
|
|
7978
8286
|
log,
|
|
7979
8287
|
failClosedFontFetch: failClosedFontFetch === true,
|
|
7980
8288
|
allowSystemFontCapture,
|
|
@@ -7982,7 +8290,7 @@ async function runCompileStage(input) {
|
|
|
7982
8290
|
variables: input.variables,
|
|
7983
8291
|
urlByAbsentLocalPath: input.urlByAbsentLocalPath,
|
|
7984
8292
|
pageMotionBlur: input.pageMotionBlur,
|
|
7985
|
-
animatedGifCacheDir: cfg.extractCacheDir ?
|
|
8293
|
+
animatedGifCacheDir: cfg.extractCacheDir ? join16(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
7986
8294
|
ffmpegProcessTimeout: cfg.ffmpegProcessTimeout
|
|
7987
8295
|
});
|
|
7988
8296
|
assertNotAborted();
|
|
@@ -8038,7 +8346,7 @@ async function runCompileStage(input) {
|
|
|
8038
8346
|
};
|
|
8039
8347
|
await preflightCompositionAssetMediaTypes({
|
|
8040
8348
|
projectDir,
|
|
8041
|
-
compiledDir:
|
|
8349
|
+
compiledDir: join16(workDir, "compiled"),
|
|
8042
8350
|
composition,
|
|
8043
8351
|
signal: abortSignal
|
|
8044
8352
|
});
|
|
@@ -8083,8 +8391,8 @@ async function runCompileStage(input) {
|
|
|
8083
8391
|
}
|
|
8084
8392
|
|
|
8085
8393
|
// src/services/render/stages/probeStage.ts
|
|
8086
|
-
import { join as
|
|
8087
|
-
import { parseHTML as
|
|
8394
|
+
import { join as join17 } from "node:path";
|
|
8395
|
+
import { parseHTML as parseHTML6 } from "linkedom";
|
|
8088
8396
|
import {
|
|
8089
8397
|
closeCaptureSession as closeCaptureSession4,
|
|
8090
8398
|
compositionRequiresWebGpu,
|
|
@@ -8131,7 +8439,7 @@ function durationToFrameCount(duration, fps) {
|
|
|
8131
8439
|
}
|
|
8132
8440
|
function hasScriptedAudioVolumeAutomation(html, audioCount) {
|
|
8133
8441
|
if (audioCount <= 0) return false;
|
|
8134
|
-
const { document: document2 } =
|
|
8442
|
+
const { document: document2 } = parseHTML6(html);
|
|
8135
8443
|
const scriptBodies = [...document2.querySelectorAll("script")].map((script) => script.textContent ?? "").join("\n");
|
|
8136
8444
|
if (!scriptBodies) return false;
|
|
8137
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(
|
|
@@ -8139,12 +8447,12 @@ function hasScriptedAudioVolumeAutomation(html, audioCount) {
|
|
|
8139
8447
|
);
|
|
8140
8448
|
}
|
|
8141
8449
|
function hasAutoStartVideos(html) {
|
|
8142
|
-
const { document: document2 } =
|
|
8450
|
+
const { document: document2 } = parseHTML6(html);
|
|
8143
8451
|
return document2.querySelector("video[data-hf-auto-start]") !== null;
|
|
8144
8452
|
}
|
|
8145
8453
|
function hasVariableBoundMedia(html, variables) {
|
|
8146
8454
|
if (!variables || Object.keys(variables).length === 0) return false;
|
|
8147
|
-
const { document: document2 } =
|
|
8455
|
+
const { document: document2 } = parseHTML6(html);
|
|
8148
8456
|
return Array.from(
|
|
8149
8457
|
document2.querySelectorAll(
|
|
8150
8458
|
"img[data-var-src], audio[data-var-src], video[data-var-src], source[data-var-src]"
|
|
@@ -8155,7 +8463,7 @@ function hasVariableBoundMedia(html, variables) {
|
|
|
8155
8463
|
});
|
|
8156
8464
|
}
|
|
8157
8465
|
function authoredMediaStarts(html) {
|
|
8158
|
-
const { document: document2 } =
|
|
8466
|
+
const { document: document2 } = parseHTML6(html);
|
|
8159
8467
|
const starts = /* @__PURE__ */ new Map();
|
|
8160
8468
|
for (const media of document2.querySelectorAll("video, audio")) {
|
|
8161
8469
|
const id = media.getAttribute(MEDIA_RENDER_ID_ATTR2) || media.getAttribute("id");
|
|
@@ -8192,7 +8500,7 @@ function reconcileBrowserMediaEnd(existingEnd, projectedEnd, sourceChanged, dura
|
|
|
8192
8500
|
return existingEnd <= 0 ? projectedEnd : Math.min(existingEnd, projectedEnd);
|
|
8193
8501
|
}
|
|
8194
8502
|
function hasRuntimeMediaChanges(html) {
|
|
8195
|
-
const { document: document2 } =
|
|
8503
|
+
const { document: document2 } = parseHTML6(html);
|
|
8196
8504
|
const scriptBodies = [...document2.querySelectorAll("script")].map(
|
|
8197
8505
|
(script) => script.textContent ?? ""
|
|
8198
8506
|
);
|
|
@@ -8275,7 +8583,7 @@ async function runProbeStage(input) {
|
|
|
8275
8583
|
});
|
|
8276
8584
|
fileServer = await createFileServer({
|
|
8277
8585
|
projectDir,
|
|
8278
|
-
compiledDir:
|
|
8586
|
+
compiledDir: join17(workDir, "compiled"),
|
|
8279
8587
|
port: 0,
|
|
8280
8588
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
8281
8589
|
fps: job.config.fps
|
|
@@ -8304,7 +8612,7 @@ async function runProbeStage(input) {
|
|
|
8304
8612
|
log.info("Creating capture session...", { attempt, maxAttempts: PROBE_MAX_ATTEMPTS });
|
|
8305
8613
|
probeSession = await createCaptureSession4(
|
|
8306
8614
|
fileServer.url,
|
|
8307
|
-
|
|
8615
|
+
join17(workDir, "probe"),
|
|
8308
8616
|
captureOpts,
|
|
8309
8617
|
null,
|
|
8310
8618
|
probeCfg
|
|
@@ -8389,7 +8697,7 @@ async function runProbeStage(input) {
|
|
|
8389
8697
|
});
|
|
8390
8698
|
probeSession = await createCaptureSession4(
|
|
8391
8699
|
fileServer.url,
|
|
8392
|
-
|
|
8700
|
+
join17(workDir, "probe-screenshot"),
|
|
8393
8701
|
captureOpts,
|
|
8394
8702
|
null,
|
|
8395
8703
|
{ ...probeCfg, forceScreenshot: true }
|
|
@@ -8425,7 +8733,7 @@ async function runProbeStage(input) {
|
|
|
8425
8733
|
compiled,
|
|
8426
8734
|
resolutions,
|
|
8427
8735
|
projectDir,
|
|
8428
|
-
|
|
8736
|
+
join17(workDir, "downloads")
|
|
8429
8737
|
);
|
|
8430
8738
|
assertNotAborted();
|
|
8431
8739
|
composition.videos = compiled.videos;
|
|
@@ -8616,7 +8924,7 @@ async function runProbeStage(input) {
|
|
|
8616
8924
|
}
|
|
8617
8925
|
await preflightCompositionAssetMediaTypes({
|
|
8618
8926
|
projectDir,
|
|
8619
|
-
compiledDir:
|
|
8927
|
+
compiledDir: join17(workDir, "compiled"),
|
|
8620
8928
|
composition,
|
|
8621
8929
|
signal: abortSignal
|
|
8622
8930
|
});
|
|
@@ -8750,7 +9058,7 @@ function validateRenderDuration(input) {
|
|
|
8750
9058
|
|
|
8751
9059
|
// src/services/render/stages/extractVideosStage.ts
|
|
8752
9060
|
import { existsSync as existsSync11 } from "node:fs";
|
|
8753
|
-
import { join as
|
|
9061
|
+
import { join as join18 } from "node:path";
|
|
8754
9062
|
import {
|
|
8755
9063
|
classifyVideoExtractionError,
|
|
8756
9064
|
createFrameLookupTable,
|
|
@@ -9065,14 +9373,14 @@ async function runExtractVideosStage(input) {
|
|
|
9065
9373
|
// because short boundary counts can differ by one frame.
|
|
9066
9374
|
{
|
|
9067
9375
|
fps: job.config.fps,
|
|
9068
|
-
outputDir:
|
|
9376
|
+
outputDir: join18(compiledDir, "__hyperframes_video_frames"),
|
|
9069
9377
|
format: job.config.videoFrameFormat ?? "auto",
|
|
9070
9378
|
toneMapHdrToSdr: job.config.hdrMode === "force-sdr",
|
|
9071
9379
|
timelineEnd: composition.duration,
|
|
9072
9380
|
maxTransientRetries: extractionPolicy.maxTransientRetries,
|
|
9073
9381
|
collectProbeFailures: extractionPolicy.failureMode === "enforce",
|
|
9074
9382
|
deferRangeExtraction,
|
|
9075
|
-
contentKeyedDirs: [
|
|
9383
|
+
contentKeyedDirs: [join18(compiledDir, REMOTE_MEDIA_SUBDIR)],
|
|
9076
9384
|
urlByAbsentLocalPath
|
|
9077
9385
|
},
|
|
9078
9386
|
abortSignal,
|
|
@@ -9139,8 +9447,8 @@ function appendAutoDetectedVideoAudio(composition, extracted) {
|
|
|
9139
9447
|
}
|
|
9140
9448
|
|
|
9141
9449
|
// src/services/render/stages/audioStage.ts
|
|
9142
|
-
import { join as
|
|
9143
|
-
import { rmSync as
|
|
9450
|
+
import { join as join20 } from "node:path";
|
|
9451
|
+
import { rmSync as rmSync10 } from "node:fs";
|
|
9144
9452
|
import {
|
|
9145
9453
|
MIXED_AUDIO_FILENAME,
|
|
9146
9454
|
audioFailureDiagnostics,
|
|
@@ -9149,8 +9457,8 @@ import {
|
|
|
9149
9457
|
|
|
9150
9458
|
// src/services/render/audioPadTrim.ts
|
|
9151
9459
|
import { spawn } from "node:child_process";
|
|
9152
|
-
import { mkdtempSync as mkdtempSync3, rmSync as
|
|
9153
|
-
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";
|
|
9154
9462
|
import {
|
|
9155
9463
|
DEFAULT_CONFIG,
|
|
9156
9464
|
describeFfmpegFailure as describeFfmpegFailure2,
|
|
@@ -9158,8 +9466,8 @@ import {
|
|
|
9158
9466
|
extractAudioMetadata as extractAudioMetadata3,
|
|
9159
9467
|
getFfprobeBinary,
|
|
9160
9468
|
ManagedChildProcess,
|
|
9161
|
-
renameWithRetry as
|
|
9162
|
-
runFfmpeg as
|
|
9469
|
+
renameWithRetry as renameWithRetry5,
|
|
9470
|
+
runFfmpeg as runFfmpeg5,
|
|
9163
9471
|
trackChildProcess
|
|
9164
9472
|
} from "@hyperframes/engine";
|
|
9165
9473
|
import { redactKnownPaths, redactTelemetryString as redactTelemetryString3 } from "@hyperframes/core";
|
|
@@ -9169,7 +9477,7 @@ import {
|
|
|
9169
9477
|
aacSegmentCount,
|
|
9170
9478
|
describeFfmpegFailure,
|
|
9171
9479
|
extractAudioMetadata as extractAudioMetadata2,
|
|
9172
|
-
runFfmpeg as
|
|
9480
|
+
runFfmpeg as runFfmpeg4
|
|
9173
9481
|
} from "@hyperframes/engine";
|
|
9174
9482
|
var toDbfs = (token) => token.toLowerCase() === "-inf" ? Number.NEGATIVE_INFINITY : Number(token);
|
|
9175
9483
|
function checkedDbfs(value) {
|
|
@@ -9193,7 +9501,7 @@ async function scanTruePeak(audioPath, trim, timeoutMs, signal) {
|
|
|
9193
9501
|
else laterBlocks = Math.max(laterBlocks, block);
|
|
9194
9502
|
}
|
|
9195
9503
|
};
|
|
9196
|
-
const result = await
|
|
9504
|
+
const result = await runFfmpeg4(
|
|
9197
9505
|
["-i", audioPath, "-map", "0:a:0", "-vn", "-af", `${trim}ebur128=peak=true`, "-f", "null", "-"],
|
|
9198
9506
|
{ signal, timeout: timeoutMs, onStderr: readLines }
|
|
9199
9507
|
);
|
|
@@ -9353,7 +9661,7 @@ async function padOrTrimAudioToVideoFrameCount(input) {
|
|
|
9353
9661
|
error: `audioPadTrim: failed to materialize ${plan.operation}: ${err instanceof Error ? err.message : String(err)}`
|
|
9354
9662
|
};
|
|
9355
9663
|
} finally {
|
|
9356
|
-
for (const path of plan.cleanupPaths)
|
|
9664
|
+
for (const path of plan.cleanupPaths) rmSync9(path, { force: true });
|
|
9357
9665
|
}
|
|
9358
9666
|
let loweredDb = 0;
|
|
9359
9667
|
if (probeTruePeak) {
|
|
@@ -9400,8 +9708,8 @@ async function constrainAacTruePeak(input) {
|
|
|
9400
9708
|
async function enforceAacTruePeak(input) {
|
|
9401
9709
|
let scratchDir;
|
|
9402
9710
|
try {
|
|
9403
|
-
scratchDir = mkdtempSync3(
|
|
9404
|
-
const correctedPath =
|
|
9711
|
+
scratchDir = mkdtempSync3(join19(dirname8(input.audioPath), ".true-peak-"));
|
|
9712
|
+
const correctedPath = join19(scratchDir, "audio.m4a");
|
|
9405
9713
|
const measurements = [];
|
|
9406
9714
|
let attenuationDb = 0;
|
|
9407
9715
|
let sourcePeakDbfs;
|
|
@@ -9416,7 +9724,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9416
9724
|
}
|
|
9417
9725
|
if (truePeakDbfs <= AAC_DELIVERY_TRUE_PEAK_DBFS) {
|
|
9418
9726
|
if (measuredPath !== correctedPath) return { success: true };
|
|
9419
|
-
|
|
9727
|
+
renameWithRetry5(correctedPath, input.audioPath);
|
|
9420
9728
|
return { success: true, loweredDb: Number((-attenuationDb).toFixed(2)) };
|
|
9421
9729
|
}
|
|
9422
9730
|
if (pass === MAX_TRUE_PEAK_CORRECTION_PASSES) break;
|
|
@@ -9457,7 +9765,7 @@ async function enforceAacTruePeak(input) {
|
|
|
9457
9765
|
)}`
|
|
9458
9766
|
};
|
|
9459
9767
|
} finally {
|
|
9460
|
-
if (scratchDir)
|
|
9768
|
+
if (scratchDir) rmSync9(scratchDir, { recursive: true, force: true });
|
|
9461
9769
|
}
|
|
9462
9770
|
}
|
|
9463
9771
|
function failResult(outputPath, target, source, error) {
|
|
@@ -9534,7 +9842,7 @@ async function defaultProbeAudioInfo(audioPath, signal) {
|
|
|
9534
9842
|
};
|
|
9535
9843
|
}
|
|
9536
9844
|
async function defaultRunFfmpeg(args, timeoutMs, signal) {
|
|
9537
|
-
return toStepResult(await
|
|
9845
|
+
return toStepResult(await runFfmpeg5(args, { signal, timeout: timeoutMs }), timeoutMs);
|
|
9538
9846
|
}
|
|
9539
9847
|
function defaultAacEncoder(timeoutMs, signal, pcmSourcePath) {
|
|
9540
9848
|
return async (inputPath, outputPath, durationSeconds, filter) => {
|
|
@@ -9604,7 +9912,7 @@ function assertAudioNotInterrupted(failures) {
|
|
|
9604
9912
|
}
|
|
9605
9913
|
async function runAudioStage(input) {
|
|
9606
9914
|
const stageStart = Date.now();
|
|
9607
|
-
const pcmSourcePath =
|
|
9915
|
+
const pcmSourcePath = join20(input.workDir, "audio-source.wav");
|
|
9608
9916
|
const duration = input.totalFrames * input.fps.den / input.fps.num;
|
|
9609
9917
|
try {
|
|
9610
9918
|
const result = await mixAudioForDelivery(input, duration, pcmSourcePath);
|
|
@@ -9624,7 +9932,7 @@ async function runAudioStage(input) {
|
|
|
9624
9932
|
}
|
|
9625
9933
|
return { ...result, audioLoweredDb, audioProcessMs: Date.now() - stageStart };
|
|
9626
9934
|
} finally {
|
|
9627
|
-
|
|
9935
|
+
rmSync10(pcmSourcePath, { force: true });
|
|
9628
9936
|
}
|
|
9629
9937
|
}
|
|
9630
9938
|
async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
@@ -9642,7 +9950,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9642
9950
|
assertNotAborted,
|
|
9643
9951
|
log
|
|
9644
9952
|
} = input;
|
|
9645
|
-
const audioOutputPath =
|
|
9953
|
+
const audioOutputPath = join20(workDir, MIXED_AUDIO_FILENAME);
|
|
9646
9954
|
let hasAudio = false;
|
|
9647
9955
|
let audioError;
|
|
9648
9956
|
let audioFailures;
|
|
@@ -9653,7 +9961,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9653
9961
|
audioResult = await processCompositionAudio(
|
|
9654
9962
|
audios,
|
|
9655
9963
|
projectDir,
|
|
9656
|
-
|
|
9964
|
+
join20(workDir, "audio-work"),
|
|
9657
9965
|
audioOutputPath,
|
|
9658
9966
|
duration,
|
|
9659
9967
|
abortSignal,
|
|
@@ -9722,7 +10030,7 @@ async function mixAudioForDelivery(input, duration, pcmSourcePath) {
|
|
|
9722
10030
|
|
|
9723
10031
|
// src/services/render/stages/captureStage.ts
|
|
9724
10032
|
import { existsSync as existsSync12, readdirSync as readdirSync3, statfsSync as statfsSync2 } from "node:fs";
|
|
9725
|
-
import { join as
|
|
10033
|
+
import { join as join21 } from "node:path";
|
|
9726
10034
|
import {
|
|
9727
10035
|
classifyCaptureFailure as classifyCaptureFailure2,
|
|
9728
10036
|
frameFileExtension,
|
|
@@ -9795,13 +10103,13 @@ function measureCaptureFrameBytes(workDir, framesDir) {
|
|
|
9795
10103
|
if (!existsSync12(workDir)) return total;
|
|
9796
10104
|
for (const attempt of readdirSync3(workDir)) {
|
|
9797
10105
|
if (/^retry-\d+-batch-\d+-worker-\d+$/.test(attempt)) {
|
|
9798
|
-
total += sampleDirectoryBytes(
|
|
10106
|
+
total += sampleDirectoryBytes(join21(workDir, attempt));
|
|
9799
10107
|
continue;
|
|
9800
10108
|
}
|
|
9801
10109
|
if (!/^capture-attempt-\d+$/.test(attempt)) continue;
|
|
9802
|
-
const attemptDir =
|
|
10110
|
+
const attemptDir = join21(workDir, attempt);
|
|
9803
10111
|
for (const name of readdirSync3(attemptDir)) {
|
|
9804
|
-
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(
|
|
10112
|
+
if (/^worker-\d+$/.test(name)) total += sampleDirectoryBytes(join21(attemptDir, name));
|
|
9805
10113
|
}
|
|
9806
10114
|
}
|
|
9807
10115
|
return total;
|
|
@@ -10094,8 +10402,8 @@ async function captureSessionFrames(session, { input, rangeStart, rangeEnd, resu
|
|
|
10094
10402
|
}
|
|
10095
10403
|
|
|
10096
10404
|
// src/services/render/stages/captureHdrStage.ts
|
|
10097
|
-
import { existsSync as existsSync14, mkdirSync as
|
|
10098
|
-
import { join as
|
|
10405
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "node:fs";
|
|
10406
|
+
import { join as join25 } from "node:path";
|
|
10099
10407
|
import {
|
|
10100
10408
|
closeCaptureSession as closeCaptureSession7,
|
|
10101
10409
|
cloneCaptureWarnings as cloneCaptureWarnings2,
|
|
@@ -10167,7 +10475,7 @@ function createHdrImageTransferCache(options = {}) {
|
|
|
10167
10475
|
}
|
|
10168
10476
|
|
|
10169
10477
|
// src/services/render/stages/captureHdrSequentialLoop.ts
|
|
10170
|
-
import { join as
|
|
10478
|
+
import { join as join22 } from "node:path";
|
|
10171
10479
|
import {
|
|
10172
10480
|
TRANSITIONS,
|
|
10173
10481
|
crossfade
|
|
@@ -10292,7 +10600,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
10292
10600
|
);
|
|
10293
10601
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
10294
10602
|
writeFileExclusiveSync(
|
|
10295
|
-
|
|
10603
|
+
join22(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
10296
10604
|
normalCanvas
|
|
10297
10605
|
);
|
|
10298
10606
|
}
|
|
@@ -10324,7 +10632,7 @@ async function runSequentialLayeredFrameLoop(input) {
|
|
|
10324
10632
|
}
|
|
10325
10633
|
|
|
10326
10634
|
// src/services/render/stages/captureHdrHybridLoop.ts
|
|
10327
|
-
import { join as
|
|
10635
|
+
import { join as join24 } from "node:path";
|
|
10328
10636
|
import {
|
|
10329
10637
|
TRANSITIONS as TRANSITIONS2,
|
|
10330
10638
|
closeCaptureSession as closeCaptureSession6,
|
|
@@ -10338,7 +10646,7 @@ import {
|
|
|
10338
10646
|
// src/services/shaderTransitionWorkerPool.ts
|
|
10339
10647
|
import { Worker } from "node:worker_threads";
|
|
10340
10648
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
10341
|
-
import { dirname as
|
|
10649
|
+
import { dirname as dirname9, join as join23 } from "node:path";
|
|
10342
10650
|
import { createRequire } from "node:module";
|
|
10343
10651
|
import { existsSync as existsSync13 } from "node:fs";
|
|
10344
10652
|
import { cpus as cpus2 } from "node:os";
|
|
@@ -10351,10 +10659,10 @@ function resolveWorkerEntry(explicit) {
|
|
|
10351
10659
|
const isTs = override.endsWith(".ts");
|
|
10352
10660
|
return { path: override, isTs };
|
|
10353
10661
|
}
|
|
10354
|
-
const moduleDir =
|
|
10355
|
-
const jsPath =
|
|
10662
|
+
const moduleDir = dirname9(fileURLToPath3(import.meta.url));
|
|
10663
|
+
const jsPath = join23(moduleDir, "shaderTransitionWorker.js");
|
|
10356
10664
|
if (existsSync13(jsPath)) return { path: jsPath, isTs: false };
|
|
10357
|
-
const tsPath =
|
|
10665
|
+
const tsPath = join23(moduleDir, "shaderTransitionWorker.ts");
|
|
10358
10666
|
return { path: tsPath, isTs: true };
|
|
10359
10667
|
}
|
|
10360
10668
|
function buildExecArgv(entryIsTs) {
|
|
@@ -10497,8 +10805,8 @@ async function createShaderTransitionWorkerPool(opts) {
|
|
|
10497
10805
|
if (terminated) {
|
|
10498
10806
|
throw new Error("shader-blend pool already terminated");
|
|
10499
10807
|
}
|
|
10500
|
-
return new Promise((
|
|
10501
|
-
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 };
|
|
10502
10810
|
const idle = slots.find((s) => !s.busy && !s.dead);
|
|
10503
10811
|
if (idle) {
|
|
10504
10812
|
queue.unshift(task);
|
|
@@ -10722,7 +11030,7 @@ async function runHybridLayeredFrameLoop(input) {
|
|
|
10722
11030
|
);
|
|
10723
11031
|
if (debugDumpEnabled && debugDumpDir && i % 30 === 0) {
|
|
10724
11032
|
writeFileExclusiveSync(
|
|
10725
|
-
|
|
11033
|
+
join24(debugDumpDir, `frame_${String(i).padStart(4, "0")}_final_rgb48le.bin`),
|
|
10726
11034
|
canvas
|
|
10727
11035
|
);
|
|
10728
11036
|
}
|
|
@@ -10910,9 +11218,9 @@ async function runCaptureHdrStage(input) {
|
|
|
10910
11218
|
if (hdrVideoFrameSources.has(v.id)) hdrVideoEndTimes.set(v.id, v.end);
|
|
10911
11219
|
}
|
|
10912
11220
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
10913
|
-
const debugDumpDir = debugDumpEnabled ?
|
|
11221
|
+
const debugDumpDir = debugDumpEnabled ? join25(framesDir, "debug-composite") : null;
|
|
10914
11222
|
if (debugDumpDir && !existsSync14(debugDumpDir)) {
|
|
10915
|
-
|
|
11223
|
+
mkdirSync8(debugDumpDir, { recursive: true });
|
|
10916
11224
|
}
|
|
10917
11225
|
const compositeTransfer = resolveCompositeTransfer(hasHdrContent, effectiveHdr);
|
|
10918
11226
|
const hdrTargetTransfer = compositeTransfer === "srgb" ? void 0 : compositeTransfer;
|
|
@@ -11065,24 +11373,24 @@ async function runCaptureHdrStage(input) {
|
|
|
11065
11373
|
|
|
11066
11374
|
// src/services/render/stages/encodeStage.ts
|
|
11067
11375
|
import {
|
|
11068
|
-
copyFileSync as
|
|
11376
|
+
copyFileSync as copyFileSync4,
|
|
11069
11377
|
existsSync as existsSync15,
|
|
11070
|
-
mkdirSync as
|
|
11378
|
+
mkdirSync as mkdirSync9,
|
|
11071
11379
|
readdirSync as readdirSync4,
|
|
11072
|
-
readFileSync as
|
|
11073
|
-
rmSync as
|
|
11074
|
-
statSync as
|
|
11075
|
-
writeFileSync as
|
|
11380
|
+
readFileSync as readFileSync8,
|
|
11381
|
+
rmSync as rmSync11,
|
|
11382
|
+
statSync as statSync7,
|
|
11383
|
+
writeFileSync as writeFileSync5
|
|
11076
11384
|
} from "node:fs";
|
|
11077
11385
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
11078
|
-
import { dirname as
|
|
11386
|
+
import { dirname as dirname10, join as join26 } from "node:path";
|
|
11079
11387
|
import {
|
|
11080
11388
|
encodeFramesChunkedConcat,
|
|
11081
11389
|
encodeFramesFromDir,
|
|
11082
11390
|
formatFfmpegError,
|
|
11083
11391
|
MIXED_AUDIO_FILENAME as MIXED_AUDIO_FILENAME2,
|
|
11084
11392
|
resolveConfig,
|
|
11085
|
-
runFfmpeg as
|
|
11393
|
+
runFfmpeg as runFfmpeg6,
|
|
11086
11394
|
spawnStreamingEncoder as spawnStreamingEncoder4
|
|
11087
11395
|
} from "@hyperframes/engine";
|
|
11088
11396
|
import {
|
|
@@ -11158,17 +11466,17 @@ function gifEncodeFailure(startTime, outputPath, error, failureReason) {
|
|
|
11158
11466
|
}
|
|
11159
11467
|
async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
|
|
11160
11468
|
const { outputPath } = argsInput;
|
|
11161
|
-
const result = await
|
|
11469
|
+
const result = await runFfmpeg6(buildGifPaletteuseArgs({ ...argsInput, wholeFrames: true }), run);
|
|
11162
11470
|
if (!result.success) {
|
|
11163
11471
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
11164
11472
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11165
11473
|
}
|
|
11166
|
-
const gif =
|
|
11474
|
+
const gif = readFileSync8(outputPath);
|
|
11167
11475
|
if (!clearGifFramesBeforeNext(gif)) {
|
|
11168
11476
|
const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
|
|
11169
11477
|
return gifEncodeFailure(startTime, outputPath, error);
|
|
11170
11478
|
}
|
|
11171
|
-
|
|
11479
|
+
writeFileSync5(outputPath, gif);
|
|
11172
11480
|
return null;
|
|
11173
11481
|
}
|
|
11174
11482
|
async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
@@ -11190,17 +11498,17 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11190
11498
|
try {
|
|
11191
11499
|
const run = { signal: input.signal, timeout: input.timeout };
|
|
11192
11500
|
for (const args of [buildGifPalettegenArgs(argsInput), buildGifPaletteuseArgs(argsInput)]) {
|
|
11193
|
-
const result = await
|
|
11501
|
+
const result = await runFfmpeg6(args, run);
|
|
11194
11502
|
if (!result.success) {
|
|
11195
11503
|
const error = formatFfmpegError(result.exitCode, result.stderr);
|
|
11196
11504
|
return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
|
|
11197
11505
|
}
|
|
11198
11506
|
}
|
|
11199
|
-
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(
|
|
11507
|
+
if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync8(outputPath)) !== false) {
|
|
11200
11508
|
const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
|
|
11201
11509
|
if (failure) return failure;
|
|
11202
11510
|
}
|
|
11203
|
-
const fileSize = existsSync15(outputPath) ?
|
|
11511
|
+
const fileSize = existsSync15(outputPath) ? statSync7(outputPath).size : 0;
|
|
11204
11512
|
return {
|
|
11205
11513
|
success: true,
|
|
11206
11514
|
outputPath,
|
|
@@ -11209,7 +11517,7 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
|
|
|
11209
11517
|
fileSize
|
|
11210
11518
|
};
|
|
11211
11519
|
} finally {
|
|
11212
|
-
|
|
11520
|
+
rmSync11(input.palettePath, { force: true });
|
|
11213
11521
|
}
|
|
11214
11522
|
}
|
|
11215
11523
|
function startEncodeProgress(job, stage, onProgress) {
|
|
@@ -11281,10 +11589,10 @@ function encodeWhileCapturing(input, totalFrames) {
|
|
|
11281
11589
|
const stream = await encoder;
|
|
11282
11590
|
for (let next = 0; next < totalFrames; next++) {
|
|
11283
11591
|
while (!landed.has(next) && !captureDone && !signal.aborted) {
|
|
11284
|
-
await new Promise((
|
|
11592
|
+
await new Promise((resolve11) => wake = resolve11);
|
|
11285
11593
|
}
|
|
11286
11594
|
if (signal.aborted) return;
|
|
11287
|
-
const mergedPath =
|
|
11595
|
+
const mergedPath = join26(input.framesDir, formatCaptureFrameName(next, "jpg"));
|
|
11288
11596
|
const frame = await readLandedFrame(landed.get(next), mergedPath);
|
|
11289
11597
|
landed.delete(next);
|
|
11290
11598
|
if (!await stream.writeFrame(frame)) {
|
|
@@ -11359,7 +11667,7 @@ async function runEncodeStage(input) {
|
|
|
11359
11667
|
const stage5Start = Date.now();
|
|
11360
11668
|
if (isPngSequence) {
|
|
11361
11669
|
const encoded = startEncodeProgress(job, "Writing PNG sequence", onProgress);
|
|
11362
|
-
if (!existsSync15(outputPath))
|
|
11670
|
+
if (!existsSync15(outputPath)) mkdirSync9(outputPath, { recursive: true });
|
|
11363
11671
|
const captured = readdirSync4(framesDir).filter((name) => name.endsWith(".png")).sort();
|
|
11364
11672
|
if (captured.length === 0) {
|
|
11365
11673
|
throw new Error(
|
|
@@ -11367,11 +11675,11 @@ async function runEncodeStage(input) {
|
|
|
11367
11675
|
);
|
|
11368
11676
|
}
|
|
11369
11677
|
captured.forEach((name, i) => {
|
|
11370
|
-
const dst =
|
|
11371
|
-
|
|
11678
|
+
const dst = join26(outputPath, formatExportFrameName(i, "png"));
|
|
11679
|
+
copyFileSync4(join26(framesDir, name), dst);
|
|
11372
11680
|
});
|
|
11373
11681
|
if (hasAudio && audioOutputPath && existsSync15(audioOutputPath)) {
|
|
11374
|
-
|
|
11682
|
+
copyFileSync4(audioOutputPath, join26(outputPath, MIXED_AUDIO_FILENAME2));
|
|
11375
11683
|
log.info(
|
|
11376
11684
|
`[Render] png-sequence: ${MIXED_AUDIO_FILENAME2} sidecar written to ${outputPath}/${MIXED_AUDIO_FILENAME2}`
|
|
11377
11685
|
);
|
|
@@ -11391,7 +11699,7 @@ async function runEncodeStage(input) {
|
|
|
11391
11699
|
const encodeResult2 = await encodeGifFromDir(framesDir, framePattern2, outputPath, {
|
|
11392
11700
|
fps: job.config.fps,
|
|
11393
11701
|
loop,
|
|
11394
|
-
palettePath:
|
|
11702
|
+
palettePath: join26(dirname10(videoOnlyPath), "gif-palette.png"),
|
|
11395
11703
|
preserveAlpha: needsAlpha,
|
|
11396
11704
|
signal: abortSignal,
|
|
11397
11705
|
timeout: engineCfg.ffmpegEncodeTimeout
|
|
@@ -11601,9 +11909,9 @@ function sampleDirectoryBytes(dir) {
|
|
|
11601
11909
|
continue;
|
|
11602
11910
|
}
|
|
11603
11911
|
for (const name of entries) {
|
|
11604
|
-
const full =
|
|
11912
|
+
const full = join27(current, name);
|
|
11605
11913
|
try {
|
|
11606
|
-
const st =
|
|
11914
|
+
const st = statSync8(full);
|
|
11607
11915
|
if (st.isDirectory()) {
|
|
11608
11916
|
stack.push(full);
|
|
11609
11917
|
} else if (st.isFile()) {
|
|
@@ -11750,8 +12058,8 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
11750
12058
|
const ranges = [];
|
|
11751
12059
|
let rangeStart = null;
|
|
11752
12060
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
11753
|
-
const framePath =
|
|
11754
|
-
const missing = !existsSync16(framePath) ||
|
|
12061
|
+
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
12062
|
+
const missing = !existsSync16(framePath) || statSync8(framePath).size <= 8;
|
|
11755
12063
|
if (missing && rangeStart === null) {
|
|
11756
12064
|
rangeStart = frameIndex;
|
|
11757
12065
|
} else if (!missing && rangeStart !== null) {
|
|
@@ -11767,7 +12075,7 @@ function findMissingFrameRanges(totalFrames, framesDir, frameExt) {
|
|
|
11767
12075
|
function buildMissingFrameRetryBatches(ranges, maxWorkers, workDir, attempt, rangeStart = 0, stride = 1) {
|
|
11768
12076
|
const workersPerBatch = Math.max(1, Math.floor(maxWorkers));
|
|
11769
12077
|
const batches = [];
|
|
11770
|
-
const outputDirFor = (batchIndex, workerId) =>
|
|
12078
|
+
const outputDirFor = (batchIndex, workerId) => join27(workDir, `retry-${attempt}-batch-${batchIndex}-worker-${workerId}`);
|
|
11771
12079
|
if (stride > 1) {
|
|
11772
12080
|
const spans = /* @__PURE__ */ new Map();
|
|
11773
12081
|
for (const range of ranges) {
|
|
@@ -11827,8 +12135,8 @@ function getNextRetryWorkerCount(currentWorkers) {
|
|
|
11827
12135
|
return Math.max(1, Math.floor(currentWorkers / 2));
|
|
11828
12136
|
}
|
|
11829
12137
|
function resolveRenderWorkDirPrefix(outputPath, jobId, platform2 = process.platform, systemTempDir = tmpdir2()) {
|
|
11830
|
-
if (platform2 === "win32" || platform2 === "darwin") return
|
|
11831
|
-
return
|
|
12138
|
+
if (platform2 === "win32" || platform2 === "darwin") return join27(systemTempDir, "hf-render-");
|
|
12139
|
+
return join27(dirname11(outputPath), `work-${jobId}-`);
|
|
11832
12140
|
}
|
|
11833
12141
|
var MAX_TRANSIENT_CAPTURE_RETRIES = 1;
|
|
11834
12142
|
function isTransientCaptureRetryEligible(failureKind, missing, retriesUsed) {
|
|
@@ -11870,7 +12178,7 @@ The composition is too large for the available memory. To reduce memory pressure
|
|
|
11870
12178
|
function countCapturedFrames(totalFrames, framesDir, frameExt) {
|
|
11871
12179
|
let captured = 0;
|
|
11872
12180
|
for (let frameIndex = 0; frameIndex < totalFrames; frameIndex++) {
|
|
11873
|
-
const framePath =
|
|
12181
|
+
const framePath = join27(framesDir, formatCaptureFrameName(frameIndex, frameExt));
|
|
11874
12182
|
if (existsSync16(framePath)) captured++;
|
|
11875
12183
|
}
|
|
11876
12184
|
return captured;
|
|
@@ -11896,7 +12204,7 @@ async function executeDiskCaptureWithAdaptiveRetry(options) {
|
|
|
11896
12204
|
reason: attempt === 0 ? "initial" : pendingTransientRetry ? "transient-retry" : "retry"
|
|
11897
12205
|
});
|
|
11898
12206
|
pendingTransientRetry = false;
|
|
11899
|
-
const attemptWorkDir =
|
|
12207
|
+
const attemptWorkDir = join27(options.workDir, `capture-attempt-${attempt}`);
|
|
11900
12208
|
const batches = missingRanges ? buildMissingFrameRetryBatches(
|
|
11901
12209
|
missingRanges,
|
|
11902
12210
|
currentWorkers,
|
|
@@ -12502,18 +12810,18 @@ function deVerifyFallbackTelemetry(err) {
|
|
|
12502
12810
|
};
|
|
12503
12811
|
}
|
|
12504
12812
|
function resolveRenderDebugDir() {
|
|
12505
|
-
const producerRoot = process.env.PRODUCER_RENDERS_DIR ?
|
|
12506
|
-
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");
|
|
12507
12815
|
}
|
|
12508
12816
|
async function executeRenderJob(job, projectDir, outputPath, progressSink, abortSignal, assertRenderActive) {
|
|
12509
12817
|
validateHlsRenderConfig(job.config);
|
|
12510
12818
|
const debugDir = resolveRenderDebugDir();
|
|
12511
|
-
const outputDir =
|
|
12512
|
-
if (!existsSync16(outputDir))
|
|
12513
|
-
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);
|
|
12514
12822
|
const pipelineStart = Date.now();
|
|
12515
12823
|
const baseLog = job.config.logger ?? defaultLogger;
|
|
12516
|
-
const logPath = job.config.debug ?
|
|
12824
|
+
const logPath = job.config.debug ? join27(workDir, "render.log") : null;
|
|
12517
12825
|
const execution = new RenderExecutionContext({
|
|
12518
12826
|
request: { renderJobId: job.id, projectDir, outputPath },
|
|
12519
12827
|
logger: logPath ? createRenderFileLogger(logPath, baseLog) : baseLog,
|
|
@@ -12527,7 +12835,7 @@ async function executeRenderJob(job, projectDir, outputPath, progressSink, abort
|
|
|
12527
12835
|
log.info("KEEP_TEMP=1, leaving workDir on disk for inspection", { workDir });
|
|
12528
12836
|
return;
|
|
12529
12837
|
}
|
|
12530
|
-
|
|
12838
|
+
rmSync12(workDir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
12531
12839
|
});
|
|
12532
12840
|
try {
|
|
12533
12841
|
await executeRenderPipeline({
|
|
@@ -12573,7 +12881,7 @@ async function executeRenderPipeline(input) {
|
|
|
12573
12881
|
imageDecodeFailures: 0
|
|
12574
12882
|
};
|
|
12575
12883
|
let hdrPerf;
|
|
12576
|
-
const perfOutputPath =
|
|
12884
|
+
const perfOutputPath = join27(workDir, "perf-summary.json");
|
|
12577
12885
|
const cfg = { ...job.config.producerConfig ?? resolveConfig2() };
|
|
12578
12886
|
const observability = new RenderObservabilityRecorder({
|
|
12579
12887
|
pipelineStartMs: pipelineStart,
|
|
@@ -12646,7 +12954,7 @@ async function executeRenderPipeline(input) {
|
|
|
12646
12954
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
12647
12955
|
assertNotAborted();
|
|
12648
12956
|
assertConfiguredFfmpegBinariesExist();
|
|
12649
|
-
if (!existsSync16(workDir))
|
|
12957
|
+
if (!existsSync16(workDir)) mkdirSync10(workDir, { recursive: true });
|
|
12650
12958
|
if (job.config.debug) {
|
|
12651
12959
|
log.info("[Render] Debug artifacts enabled", { workDir, logPath });
|
|
12652
12960
|
}
|
|
@@ -12675,22 +12983,22 @@ async function executeRenderPipeline(input) {
|
|
|
12675
12983
|
requestedWorkers: job.config.workers ?? "auto"
|
|
12676
12984
|
});
|
|
12677
12985
|
const entryFile = job.config.entryFile || "index.html";
|
|
12678
|
-
let htmlPath =
|
|
12986
|
+
let htmlPath = join27(projectDir, entryFile);
|
|
12679
12987
|
if (!existsSync16(htmlPath)) {
|
|
12680
12988
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
12681
12989
|
}
|
|
12682
12990
|
assertNotAborted();
|
|
12683
|
-
const rawEntry =
|
|
12991
|
+
const rawEntry = readFileSync9(htmlPath, "utf-8");
|
|
12684
12992
|
if (entryFile !== "index.html" && isTemplateEntry(rawEntry)) {
|
|
12685
|
-
const wrapperPath =
|
|
12686
|
-
const projectIndexPath =
|
|
12993
|
+
const wrapperPath = join27(workDir, "standalone-entry.html");
|
|
12994
|
+
const projectIndexPath = join27(projectDir, "index.html");
|
|
12687
12995
|
if (!existsSync16(projectIndexPath)) {
|
|
12688
12996
|
throw new Error(
|
|
12689
12997
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
12690
12998
|
);
|
|
12691
12999
|
}
|
|
12692
13000
|
const standaloneHtml = extractStandaloneEntryFromIndex(
|
|
12693
|
-
|
|
13001
|
+
readFileSync9(projectIndexPath, "utf-8"),
|
|
12694
13002
|
entryFile,
|
|
12695
13003
|
rawEntry
|
|
12696
13004
|
);
|
|
@@ -12699,7 +13007,7 @@ async function executeRenderPipeline(input) {
|
|
|
12699
13007
|
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
12700
13008
|
);
|
|
12701
13009
|
}
|
|
12702
|
-
|
|
13010
|
+
writeFileSync6(wrapperPath, standaloneHtml, "utf-8");
|
|
12703
13011
|
htmlPath = wrapperPath;
|
|
12704
13012
|
log.info("Extracted standalone entry from index.html host context", {
|
|
12705
13013
|
entryFile
|
|
@@ -12846,7 +13154,7 @@ async function executeRenderPipeline(input) {
|
|
|
12846
13154
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress, {
|
|
12847
13155
|
code: "extract_video"
|
|
12848
13156
|
});
|
|
12849
|
-
const compiledDir =
|
|
13157
|
+
const compiledDir = join27(workDir, "compiled");
|
|
12850
13158
|
const extractResult = await observeRenderStage(
|
|
12851
13159
|
observability,
|
|
12852
13160
|
"video_extract",
|
|
@@ -12980,7 +13288,7 @@ async function executeRenderPipeline(input) {
|
|
|
12980
13288
|
try {
|
|
12981
13289
|
fileServer = await createFileServer({
|
|
12982
13290
|
projectDir,
|
|
12983
|
-
compiledDir:
|
|
13291
|
+
compiledDir: join27(workDir, "compiled"),
|
|
12984
13292
|
port: 0,
|
|
12985
13293
|
preHeadScripts: [VIRTUAL_TIME_SHIM],
|
|
12986
13294
|
fps: job.config.fps
|
|
@@ -12998,8 +13306,8 @@ async function executeRenderPipeline(input) {
|
|
|
12998
13306
|
if (!activeFileServer) {
|
|
12999
13307
|
throw new Error("File server failed to initialize before frame capture");
|
|
13000
13308
|
}
|
|
13001
|
-
const framesDir =
|
|
13002
|
-
if (!existsSync16(framesDir))
|
|
13309
|
+
const framesDir = join27(workDir, "captured-frames");
|
|
13310
|
+
if (!existsSync16(framesDir)) mkdirSync10(framesDir, { recursive: true });
|
|
13003
13311
|
updateJobStatus(job, "rendering", "Checking browser GPU", 25, onProgress, {
|
|
13004
13312
|
code: "start_browsers"
|
|
13005
13313
|
});
|
|
@@ -13470,7 +13778,7 @@ async function executeRenderPipeline(input) {
|
|
|
13470
13778
|
hls: ".mp4"
|
|
13471
13779
|
};
|
|
13472
13780
|
const videoExt = FORMAT_EXT[outputFormat] ?? ".mp4";
|
|
13473
|
-
const videoOnlyPath =
|
|
13781
|
+
const videoOnlyPath = join27(workDir, `video-only${videoExt}`);
|
|
13474
13782
|
const usePageSideCompositingForTransitions = (cfg.enablePageSideCompositing || isGif) && compiled.hasShaderTransitions && !hasHdrContent && outputSupportsPageSideShaderCompositing(outputFormat);
|
|
13475
13783
|
if (usePageSideCompositingForTransitions) {
|
|
13476
13784
|
activeFileServer.addPreHeadScript(HF_PAGE_SIDE_COMPOSITING_STUB);
|
|
@@ -13723,7 +14031,7 @@ async function executeRenderPipeline(input) {
|
|
|
13723
14031
|
pageMotionBlur: job.config.pageMotionBlur,
|
|
13724
14032
|
captureQuality: captureOptions.quality
|
|
13725
14033
|
});
|
|
13726
|
-
const segmentDir = segmentDirFor(
|
|
14034
|
+
const segmentDir = segmentDirFor(join27(projectDir, "renders"), segmentPlanHash);
|
|
13727
14035
|
let completedSegments = /* @__PURE__ */ new Set();
|
|
13728
14036
|
if (job.config.resumeSegments === true) {
|
|
13729
14037
|
const existing = readSegmentManifest(segmentDir);
|
|
@@ -13738,10 +14046,10 @@ async function executeRenderPipeline(input) {
|
|
|
13738
14046
|
});
|
|
13739
14047
|
}
|
|
13740
14048
|
if (completedSegments.size === 0) {
|
|
13741
|
-
|
|
14049
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13742
14050
|
}
|
|
13743
14051
|
} else {
|
|
13744
|
-
|
|
14052
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13745
14053
|
}
|
|
13746
14054
|
const segmentManifest = readSegmentManifest(segmentDir) ?? {
|
|
13747
14055
|
version: 1,
|
|
@@ -13822,7 +14130,7 @@ async function executeRenderPipeline(input) {
|
|
|
13822
14130
|
);
|
|
13823
14131
|
updateCaptureObservability({ segmentRetries: segmentedRes.segmentRetries });
|
|
13824
14132
|
if (job.config.keepSegments !== true) {
|
|
13825
|
-
|
|
14133
|
+
rmSync12(segmentDir, { recursive: true, force: true });
|
|
13826
14134
|
}
|
|
13827
14135
|
} else {
|
|
13828
14136
|
capturePlan = replanAfterFailure(capturePlan, { kind: "streaming_unavailable" });
|
|
@@ -14094,8 +14402,8 @@ async function executeRenderPipeline(input) {
|
|
|
14094
14402
|
"capture_disk",
|
|
14095
14403
|
"drawElement capture failed; retrying with a fresh screenshot session"
|
|
14096
14404
|
);
|
|
14097
|
-
|
|
14098
|
-
|
|
14405
|
+
rmSync12(framesDir, { recursive: true, force: true });
|
|
14406
|
+
mkdirSync10(framesDir, { recursive: true });
|
|
14099
14407
|
resetCaptureAttemptProgress(job);
|
|
14100
14408
|
dedupPerfs.length = 0;
|
|
14101
14409
|
cfg.useDrawElement = false;
|
|
@@ -14271,7 +14579,7 @@ async function executeRenderPipeline(input) {
|
|
|
14271
14579
|
job.perfSummary = perfSummary;
|
|
14272
14580
|
if (job.config.debug) {
|
|
14273
14581
|
try {
|
|
14274
|
-
|
|
14582
|
+
writeFileSync6(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
|
|
14275
14583
|
} catch (err) {
|
|
14276
14584
|
log.debug("Failed to write perf summary", {
|
|
14277
14585
|
perfOutputPath,
|
|
@@ -14281,8 +14589,8 @@ async function executeRenderPipeline(input) {
|
|
|
14281
14589
|
}
|
|
14282
14590
|
if (job.config.debug) {
|
|
14283
14591
|
if (!isPngSequence && !isHls && existsSync16(stagedOutputPath)) {
|
|
14284
|
-
const debugOutput =
|
|
14285
|
-
|
|
14592
|
+
const debugOutput = join27(workDir, `output${videoExt}`);
|
|
14593
|
+
copyFileSync5(stagedOutputPath, debugOutput);
|
|
14286
14594
|
}
|
|
14287
14595
|
}
|
|
14288
14596
|
await commitArtifactTransaction(artifactTransaction, () => {
|
|
@@ -14387,14 +14695,14 @@ async function executeRenderPipeline(input) {
|
|
|
14387
14695
|
// src/services/hyperframeLint.ts
|
|
14388
14696
|
import {
|
|
14389
14697
|
existsSync as existsSync17,
|
|
14390
|
-
readFileSync as
|
|
14391
|
-
statSync as
|
|
14392
|
-
openSync as
|
|
14698
|
+
readFileSync as readFileSync10,
|
|
14699
|
+
statSync as statSync9,
|
|
14700
|
+
openSync as openSync5,
|
|
14393
14701
|
fstatSync as fstatSync3,
|
|
14394
|
-
closeSync as
|
|
14702
|
+
closeSync as closeSync5,
|
|
14395
14703
|
constants as constants3
|
|
14396
14704
|
} from "node:fs";
|
|
14397
|
-
import { resolve as
|
|
14705
|
+
import { resolve as resolve9, join as join28, relative as relative3, isAbsolute as isAbsolute3, sep as sep2 } from "node:path";
|
|
14398
14706
|
import { lintHyperframeHtml } from "@hyperframes/lint";
|
|
14399
14707
|
function isStringRecord(value) {
|
|
14400
14708
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -14424,30 +14732,30 @@ function pickEntryFile(files, preferredEntryFile) {
|
|
|
14424
14732
|
function readEntryHtml(filePath) {
|
|
14425
14733
|
let fd;
|
|
14426
14734
|
try {
|
|
14427
|
-
fd =
|
|
14735
|
+
fd = openSync5(filePath, constants3.O_RDONLY | constants3.O_NONBLOCK);
|
|
14428
14736
|
} catch (error) {
|
|
14429
14737
|
if (error instanceof Error && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR"))
|
|
14430
14738
|
return null;
|
|
14431
|
-
if (!
|
|
14739
|
+
if (!statSync9(filePath, { throwIfNoEntry: false })?.isFile()) return null;
|
|
14432
14740
|
throw error;
|
|
14433
14741
|
}
|
|
14434
14742
|
try {
|
|
14435
14743
|
if (!fstatSync3(fd).isFile()) return null;
|
|
14436
|
-
return
|
|
14744
|
+
return readFileSync10(fd, "utf-8");
|
|
14437
14745
|
} finally {
|
|
14438
|
-
|
|
14746
|
+
closeSync5(fd);
|
|
14439
14747
|
}
|
|
14440
14748
|
}
|
|
14441
14749
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
14442
|
-
const absProjectDir =
|
|
14443
|
-
if (!existsSync17(absProjectDir) || !
|
|
14750
|
+
const absProjectDir = resolve9(projectDir);
|
|
14751
|
+
if (!existsSync17(absProjectDir) || !statSync9(absProjectDir).isDirectory()) {
|
|
14444
14752
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
14445
14753
|
}
|
|
14446
14754
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
14447
14755
|
(value) => typeof value === "string" && value.trim().length > 0
|
|
14448
14756
|
);
|
|
14449
14757
|
for (const entryFile of entryCandidates) {
|
|
14450
|
-
const absoluteEntryPath =
|
|
14758
|
+
const absoluteEntryPath = resolve9(absProjectDir, entryFile);
|
|
14451
14759
|
const entryRelativePath = relative3(absProjectDir, absoluteEntryPath);
|
|
14452
14760
|
if (entryRelativePath === ".." || entryRelativePath.startsWith(`..${sep2}`) || isAbsolute3(entryRelativePath)) {
|
|
14453
14761
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
@@ -14462,7 +14770,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
14462
14770
|
}
|
|
14463
14771
|
}
|
|
14464
14772
|
return {
|
|
14465
|
-
error: `No HTML entry file found in project directory: ${
|
|
14773
|
+
error: `No HTML entry file found in project directory: ${join28(absProjectDir, preferredEntryFile || "index.html")}`
|
|
14466
14774
|
};
|
|
14467
14775
|
}
|
|
14468
14776
|
function prepareHyperframeLintBody(body) {
|
|
@@ -14508,7 +14816,7 @@ async function runHyperframeLint(prepared) {
|
|
|
14508
14816
|
// src/services/healthWorker.ts
|
|
14509
14817
|
import { Worker as Worker2 } from "node:worker_threads";
|
|
14510
14818
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
14511
|
-
import { dirname as
|
|
14819
|
+
import { dirname as dirname12, join as join29 } from "node:path";
|
|
14512
14820
|
import { existsSync as existsSync18 } from "node:fs";
|
|
14513
14821
|
var DEFAULT_HEALTH_PORT = 9848;
|
|
14514
14822
|
async function startHealthWorker(options = {}) {
|
|
@@ -14527,12 +14835,12 @@ async function startHealthWorker(options = {}) {
|
|
|
14527
14835
|
stdout: false,
|
|
14528
14836
|
stderr: false
|
|
14529
14837
|
});
|
|
14530
|
-
await new Promise((
|
|
14838
|
+
await new Promise((resolve11, reject) => {
|
|
14531
14839
|
const onMessage = (msg) => {
|
|
14532
14840
|
if (msg?.type === "listening") {
|
|
14533
14841
|
worker.off("error", onError);
|
|
14534
14842
|
worker.off("message", onMessage);
|
|
14535
|
-
|
|
14843
|
+
resolve11();
|
|
14536
14844
|
} else if (msg?.type === "listen-error") {
|
|
14537
14845
|
worker.off("error", onError);
|
|
14538
14846
|
worker.off("message", onMessage);
|
|
@@ -14583,8 +14891,8 @@ function defaultLogger2() {
|
|
|
14583
14891
|
};
|
|
14584
14892
|
}
|
|
14585
14893
|
function resolveWorkerEntry2() {
|
|
14586
|
-
const here =
|
|
14587
|
-
const candidates = [
|
|
14894
|
+
const here = dirname12(fileURLToPath5(import.meta.url));
|
|
14895
|
+
const candidates = [join29(here, "healthWorkerThread.js"), join29(here, "healthWorkerThread.ts")];
|
|
14588
14896
|
for (const candidate of candidates) {
|
|
14589
14897
|
if (existsSync18(candidate)) return candidate;
|
|
14590
14898
|
}
|
|
@@ -14620,7 +14928,7 @@ import { DISTRIBUTED_FPS, isDistributedFps } from "@hyperframes/core";
|
|
|
14620
14928
|
|
|
14621
14929
|
// src/services/distributed/plan.ts
|
|
14622
14930
|
import { audioFailureDiagnostics as audioFailureDiagnostics3 } from "@hyperframes/engine";
|
|
14623
|
-
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";
|
|
14624
14932
|
import {
|
|
14625
14933
|
fpsToNumber as fpsToNumber10,
|
|
14626
14934
|
HF_COLOR_GRADING_ATTR as HF_COLOR_GRADING_ATTR2
|
|
@@ -14630,7 +14938,7 @@ import {
|
|
|
14630
14938
|
normalizeVp9CpuUsed,
|
|
14631
14939
|
resolveConfig as resolveConfig3,
|
|
14632
14940
|
declareContentAddressedUrl,
|
|
14633
|
-
renameWithRetry as
|
|
14941
|
+
renameWithRetry as renameWithRetry7,
|
|
14634
14942
|
declareProbeFactsUrl
|
|
14635
14943
|
} from "@hyperframes/engine";
|
|
14636
14944
|
|
|
@@ -14701,10 +15009,10 @@ var DISTRIBUTED_RENDER_CAPABILITIES = Object.freeze({
|
|
|
14701
15009
|
var FIELD_DELIMITER = Buffer.from([0]);
|
|
14702
15010
|
|
|
14703
15011
|
// src/services/distributed/chunkAudio.ts
|
|
14704
|
-
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as
|
|
15012
|
+
import { appendRenderProvenanceArgs, describeFfmpegFailure as describeFfmpegFailure3, runFfmpeg as runFfmpeg7 } from "@hyperframes/engine";
|
|
14705
15013
|
|
|
14706
15014
|
// src/services/distributed/mp4Stitch.ts
|
|
14707
|
-
import { renameWithRetry as
|
|
15015
|
+
import { renameWithRetry as renameWithRetry6 } from "@hyperframes/engine";
|
|
14708
15016
|
var SCAN_BYTES = 64 * 1024;
|
|
14709
15017
|
|
|
14710
15018
|
// src/services/distributed/chunkAudio.ts
|
|
@@ -14724,9 +15032,9 @@ var PLAN_ROOT_CATEGORY = {
|
|
|
14724
15032
|
var PLAN_DIR_SIZE_LIMIT_BYTES = 2 * 1024 * 1024 * 1024;
|
|
14725
15033
|
var FREEZE_OWNED_PLAN_FILES = [
|
|
14726
15034
|
"plan.json",
|
|
14727
|
-
|
|
14728
|
-
|
|
14729
|
-
|
|
15035
|
+
join30("meta", "composition.json"),
|
|
15036
|
+
join30("meta", "encoder.json"),
|
|
15037
|
+
join30("meta", "chunks.json")
|
|
14730
15038
|
];
|
|
14731
15039
|
|
|
14732
15040
|
// src/services/distributed/renderConfigValidation.ts
|
|
@@ -14957,6 +15265,8 @@ var DEFAULT_SERVER_FPS = { num: 30, den: 1 };
|
|
|
14957
15265
|
var SSE_KEEPALIVE_MS = 3e4;
|
|
14958
15266
|
var SAFE_RENDER_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
14959
15267
|
"ASSET_MEDIA_TYPE_MISMATCH",
|
|
15268
|
+
"HEIF_IMAGE_CONVERSION_FAILED",
|
|
15269
|
+
"HEIF_TOOL_UNAVAILABLE",
|
|
14960
15270
|
"NOT_MEDIA_PAYLOAD",
|
|
14961
15271
|
"INVALID_VIDEO_METADATA",
|
|
14962
15272
|
"VIDEO_SOURCE_UNRENDERABLE",
|
|
@@ -15092,8 +15402,8 @@ function buildRenderJobConfig(input, outputPath, log) {
|
|
|
15092
15402
|
function resolvePreparedRenderOutput(prepared, rendersDir, log) {
|
|
15093
15403
|
const { input, cleanupProjectDir } = prepared;
|
|
15094
15404
|
const absoluteOutputPath = resolveOutputPath(input.projectDir, input.outputPath, rendersDir, log);
|
|
15095
|
-
const outputDir =
|
|
15096
|
-
if (!existsSync19(outputDir))
|
|
15405
|
+
const outputDir = dirname13(absoluteOutputPath);
|
|
15406
|
+
if (!existsSync19(outputDir)) mkdirSync11(outputDir, { recursive: true });
|
|
15097
15407
|
return { input, cleanupProjectDir, absoluteOutputPath };
|
|
15098
15408
|
}
|
|
15099
15409
|
function validateRenderOverrides(body) {
|
|
@@ -15133,12 +15443,12 @@ var MAX_PREVIEW_HTML_BYTES = 20 * 1024 * 1024;
|
|
|
15133
15443
|
function prepareProjectDirectory(projectDir, options) {
|
|
15134
15444
|
const candidate = nonEmptyString(projectDir);
|
|
15135
15445
|
if (!candidate) return null;
|
|
15136
|
-
const absProjectDir =
|
|
15137
|
-
if (!existsSync19(absProjectDir) || !
|
|
15446
|
+
const absProjectDir = resolve10(candidate);
|
|
15447
|
+
if (!existsSync19(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
|
|
15138
15448
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
15139
15449
|
}
|
|
15140
15450
|
const entry = options.entryFile || "index.html";
|
|
15141
|
-
if (!existsSync19(
|
|
15451
|
+
if (!existsSync19(resolve10(absProjectDir, entry))) {
|
|
15142
15452
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
15143
15453
|
}
|
|
15144
15454
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -15159,8 +15469,8 @@ async function resolveInlineRenderHtml(body) {
|
|
|
15159
15469
|
}
|
|
15160
15470
|
function materializeInlineProject(html, options) {
|
|
15161
15471
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
|
|
15162
|
-
const tempProjectDir = mkdtempSync4(
|
|
15163
|
-
|
|
15472
|
+
const tempProjectDir = mkdtempSync4(join31(tempRoot, "producer-project-"));
|
|
15473
|
+
writeFileSync7(join31(tempProjectDir, "index.html"), html, "utf-8");
|
|
15164
15474
|
return {
|
|
15165
15475
|
prepared: {
|
|
15166
15476
|
input: { projectDir: tempProjectDir, ...options },
|
|
@@ -15185,7 +15495,7 @@ function resolveOutputPath(projectDir, outputCandidate, rendersDir, log) {
|
|
|
15185
15495
|
try {
|
|
15186
15496
|
return resolveRenderPaths(projectDir, outputCandidate, rendersDir).absoluteOutputPath;
|
|
15187
15497
|
} catch (error) {
|
|
15188
|
-
const fallbackPath =
|
|
15498
|
+
const fallbackPath = resolve10(rendersDir, `producer-fallback-${Date.now()}.mp4`);
|
|
15189
15499
|
log.warn("Failed to resolve output path, using fallback", {
|
|
15190
15500
|
fallback: fallbackPath,
|
|
15191
15501
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -15221,7 +15531,7 @@ function createArtifactStore(ttlMs) {
|
|
|
15221
15531
|
function cleanupTempDir(dir, log) {
|
|
15222
15532
|
if (!dir) return;
|
|
15223
15533
|
try {
|
|
15224
|
-
|
|
15534
|
+
rmSync13(dir, { recursive: true, force: true });
|
|
15225
15535
|
} catch (error) {
|
|
15226
15536
|
log.warn("Failed to cleanup temp project dir", {
|
|
15227
15537
|
cleanupProjectDir: dir,
|
|
@@ -15230,7 +15540,7 @@ function cleanupTempDir(dir, log) {
|
|
|
15230
15540
|
}
|
|
15231
15541
|
}
|
|
15232
15542
|
function outputFileSize(path) {
|
|
15233
|
-
return existsSync19(path) ?
|
|
15543
|
+
return existsSync19(path) ? statSync10(path).size : 0;
|
|
15234
15544
|
}
|
|
15235
15545
|
function createBlockingProgressReporter(log, requestId) {
|
|
15236
15546
|
let lastLoggedPct = -10;
|
|
@@ -15536,7 +15846,7 @@ function createRenderHandlers(options = {}) {
|
|
|
15536
15846
|
store.delete(token);
|
|
15537
15847
|
return c.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
15538
15848
|
}
|
|
15539
|
-
const stats =
|
|
15849
|
+
const stats = statSync10(artifact.path);
|
|
15540
15850
|
return new Response(createReadStream3(artifact.path), {
|
|
15541
15851
|
headers: {
|
|
15542
15852
|
"content-type": "video/mp4",
|
|
@@ -15603,7 +15913,7 @@ function startServer(options = {}) {
|
|
|
15603
15913
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
15604
15914
|
return server;
|
|
15605
15915
|
}
|
|
15606
|
-
var entryScript = process.argv[1] ?
|
|
15916
|
+
var entryScript = process.argv[1] ? resolve10(process.argv[1]) : "";
|
|
15607
15917
|
var isPublicServerEntry = entryScript.endsWith("/public-server.js") || entryScript.endsWith("/src/server.ts");
|
|
15608
15918
|
if (isPublicServerEntry) {
|
|
15609
15919
|
const { values } = parseArgs({
|