hyperframes 0.4.19 → 0.4.21
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/cli.js +1077 -528
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -54,7 +54,7 @@ var VERSION;
|
|
|
54
54
|
var init_version = __esm({
|
|
55
55
|
"src/version.ts"() {
|
|
56
56
|
"use strict";
|
|
57
|
-
VERSION = true ? "0.4.
|
|
57
|
+
VERSION = true ? "0.4.21" : "0.0.0-dev";
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -4801,6 +4801,7 @@ function extractGsapWindows(script) {
|
|
|
4801
4801
|
position: animation.position,
|
|
4802
4802
|
end: animation.position + meta.effectiveDuration,
|
|
4803
4803
|
properties: meta.properties.length > 0 ? meta.properties : Object.keys(animation.properties),
|
|
4804
|
+
propertyValues: meta.propertyValues,
|
|
4804
4805
|
overwriteAuto: meta.overwriteAuto,
|
|
4805
4806
|
method: match[1] ?? "to",
|
|
4806
4807
|
raw
|
|
@@ -4809,8 +4810,14 @@ function extractGsapWindows(script) {
|
|
|
4809
4810
|
return windows;
|
|
4810
4811
|
}
|
|
4811
4812
|
function parseGsapWindowMeta(method, argsStr) {
|
|
4813
|
+
const emptyMeta = {
|
|
4814
|
+
effectiveDuration: 0,
|
|
4815
|
+
properties: [],
|
|
4816
|
+
propertyValues: {},
|
|
4817
|
+
overwriteAuto: false
|
|
4818
|
+
};
|
|
4812
4819
|
const selectorMatch = argsStr.match(/^\s*["']([^"']+)["']\s*,/);
|
|
4813
|
-
if (!selectorMatch) return
|
|
4820
|
+
if (!selectorMatch) return emptyMeta;
|
|
4814
4821
|
const afterSelector = argsStr.slice(selectorMatch[0].length);
|
|
4815
4822
|
let properties = {};
|
|
4816
4823
|
let fromProperties = {};
|
|
@@ -4848,6 +4855,7 @@ function parseGsapWindowMeta(method, argsStr) {
|
|
|
4848
4855
|
return {
|
|
4849
4856
|
effectiveDuration: method === "set" ? 0 : effectiveDuration,
|
|
4850
4857
|
properties: [...propertyNames],
|
|
4858
|
+
propertyValues: properties,
|
|
4851
4859
|
overwriteAuto
|
|
4852
4860
|
};
|
|
4853
4861
|
}
|
|
@@ -4895,6 +4903,94 @@ function stringValue(value) {
|
|
|
4895
4903
|
if (typeof value === "number") return String(value);
|
|
4896
4904
|
return null;
|
|
4897
4905
|
}
|
|
4906
|
+
function zeroValue(value) {
|
|
4907
|
+
if (typeof value === "number") return value === 0;
|
|
4908
|
+
if (typeof value !== "string") return false;
|
|
4909
|
+
return Number(value.trim()) === 0;
|
|
4910
|
+
}
|
|
4911
|
+
function isHiddenGsapState(values) {
|
|
4912
|
+
const visibility = stringValue(values.visibility)?.toLowerCase();
|
|
4913
|
+
const display = stringValue(values.display)?.toLowerCase();
|
|
4914
|
+
return zeroValue(values.opacity) || zeroValue(values.autoAlpha) || visibility === "hidden" || display === "none";
|
|
4915
|
+
}
|
|
4916
|
+
function isSceneBoundaryExit(win) {
|
|
4917
|
+
if (win.end <= win.position) return false;
|
|
4918
|
+
if (win.method !== "to" && win.method !== "fromTo") return false;
|
|
4919
|
+
return isHiddenGsapState(win.propertyValues);
|
|
4920
|
+
}
|
|
4921
|
+
function isHardKillSet(win, selector, boundary) {
|
|
4922
|
+
return win.method === "set" && win.targetSelector === selector && Math.abs(win.position - boundary) <= SCENE_BOUNDARY_EPSILON_SECONDS && isHiddenGsapState(win.propertyValues);
|
|
4923
|
+
}
|
|
4924
|
+
function hiddenStateLiteral(values) {
|
|
4925
|
+
if (zeroValue(values.autoAlpha)) return "{ autoAlpha: 0 }";
|
|
4926
|
+
if (zeroValue(values.opacity)) return "{ opacity: 0 }";
|
|
4927
|
+
if (stringValue(values.visibility)?.toLowerCase() === "hidden") return '{ visibility: "hidden" }';
|
|
4928
|
+
if (stringValue(values.display)?.toLowerCase() === "none") return '{ display: "none" }';
|
|
4929
|
+
return "{ opacity: 0 }";
|
|
4930
|
+
}
|
|
4931
|
+
function findTagEnd(source, tag) {
|
|
4932
|
+
const escapedTagName = tag.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4933
|
+
const pattern = new RegExp(`<\\/?${escapedTagName}\\b[^>]*>`, "gi");
|
|
4934
|
+
pattern.lastIndex = tag.index;
|
|
4935
|
+
let depth = 0;
|
|
4936
|
+
let match;
|
|
4937
|
+
while ((match = pattern.exec(source)) !== null) {
|
|
4938
|
+
const raw = match[0];
|
|
4939
|
+
const isClosing = /^<\s*\//.test(raw);
|
|
4940
|
+
const isSelfClosing = /\/\s*>$/.test(raw);
|
|
4941
|
+
if (!isClosing && !isSelfClosing) depth += 1;
|
|
4942
|
+
if (isClosing) depth -= 1;
|
|
4943
|
+
if (depth === 0) return pattern.lastIndex;
|
|
4944
|
+
}
|
|
4945
|
+
return source.length;
|
|
4946
|
+
}
|
|
4947
|
+
function collectCompositionRanges(source, tags) {
|
|
4948
|
+
return tags.map((tag) => {
|
|
4949
|
+
const id = readAttr(tag.raw, "data-composition-id");
|
|
4950
|
+
if (!id) return null;
|
|
4951
|
+
return {
|
|
4952
|
+
id,
|
|
4953
|
+
start: tag.index,
|
|
4954
|
+
end: findTagEnd(source, tag)
|
|
4955
|
+
};
|
|
4956
|
+
}).filter((range) => range !== null);
|
|
4957
|
+
}
|
|
4958
|
+
function findContainingCompositionId(tag, ranges) {
|
|
4959
|
+
let match = null;
|
|
4960
|
+
for (const range of ranges) {
|
|
4961
|
+
if (tag.index < range.start || tag.index >= range.end) continue;
|
|
4962
|
+
if (!match || range.start >= match.start) match = range;
|
|
4963
|
+
}
|
|
4964
|
+
return match?.id || null;
|
|
4965
|
+
}
|
|
4966
|
+
function collectClipStartBoundariesByComposition(source, tags) {
|
|
4967
|
+
const ranges = collectCompositionRanges(source, tags);
|
|
4968
|
+
const boundaries = /* @__PURE__ */ new Map();
|
|
4969
|
+
for (const tag of tags) {
|
|
4970
|
+
const classAttr = readAttr(tag.raw, "class") || "";
|
|
4971
|
+
const classes = classAttr.split(/\s+/).filter(Boolean);
|
|
4972
|
+
if (!classes.includes("clip")) continue;
|
|
4973
|
+
const compositionId = findContainingCompositionId(tag, ranges);
|
|
4974
|
+
if (!compositionId) continue;
|
|
4975
|
+
const start = numberValue(readAttr(tag.raw, "data-start") ?? void 0);
|
|
4976
|
+
if (start == null || start <= 0) continue;
|
|
4977
|
+
const compositionBoundaries = boundaries.get(compositionId) ?? /* @__PURE__ */ new Set();
|
|
4978
|
+
compositionBoundaries.add(start);
|
|
4979
|
+
boundaries.set(compositionId, compositionBoundaries);
|
|
4980
|
+
}
|
|
4981
|
+
return new Map(
|
|
4982
|
+
[...boundaries.entries()].map(([compositionId, values]) => [
|
|
4983
|
+
compositionId,
|
|
4984
|
+
[...values].sort((a, b) => a - b)
|
|
4985
|
+
])
|
|
4986
|
+
);
|
|
4987
|
+
}
|
|
4988
|
+
function findMatchingSceneBoundary(time, boundaries) {
|
|
4989
|
+
for (const boundary of boundaries) {
|
|
4990
|
+
if (Math.abs(time - boundary) <= SCENE_BOUNDARY_EPSILON_SECONDS) return boundary;
|
|
4991
|
+
}
|
|
4992
|
+
return null;
|
|
4993
|
+
}
|
|
4898
4994
|
function isSuspiciousGlobalSelector(selector) {
|
|
4899
4995
|
if (!selector) return false;
|
|
4900
4996
|
if (selector.includes("[data-composition-id=")) return false;
|
|
@@ -4933,16 +5029,17 @@ function cssTransformToGsapProps(cssTransform) {
|
|
|
4933
5029
|
}
|
|
4934
5030
|
return parts.length > 0 ? parts.join(", ") : null;
|
|
4935
5031
|
}
|
|
4936
|
-
var META_GSAP_KEYS, gsapRules;
|
|
5032
|
+
var META_GSAP_KEYS, SCENE_BOUNDARY_EPSILON_SECONDS, gsapRules;
|
|
4937
5033
|
var init_gsap = __esm({
|
|
4938
5034
|
"../core/src/lint/rules/gsap.ts"() {
|
|
4939
5035
|
"use strict";
|
|
4940
5036
|
init_gsapParser();
|
|
4941
5037
|
init_utils();
|
|
4942
5038
|
META_GSAP_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "repeat", "yoyo", "overwrite", "delay"]);
|
|
5039
|
+
SCENE_BOUNDARY_EPSILON_SECONDS = 0.05;
|
|
4943
5040
|
gsapRules = [
|
|
4944
5041
|
// overlapping_gsap_tweens + gsap_animates_clip_element + unscoped_gsap_selector
|
|
4945
|
-
({ tags, scripts, rootCompositionId }) => {
|
|
5042
|
+
({ source, tags, scripts, rootCompositionId }) => {
|
|
4946
5043
|
const findings = [];
|
|
4947
5044
|
const clipIds = /* @__PURE__ */ new Map();
|
|
4948
5045
|
const clipClasses = /* @__PURE__ */ new Map();
|
|
@@ -4962,9 +5059,11 @@ var init_gsap = __esm({
|
|
|
4962
5059
|
}
|
|
4963
5060
|
}
|
|
4964
5061
|
const classUsage = countClassUsage(tags);
|
|
5062
|
+
const clipStartBoundariesByComposition = collectClipStartBoundariesByComposition(source, tags);
|
|
4965
5063
|
for (const script of scripts) {
|
|
4966
5064
|
const localTimelineCompId = readRegisteredTimelineCompositionId(script.content);
|
|
4967
5065
|
const gsapWindows = extractGsapWindows(script.content);
|
|
5066
|
+
const clipStartBoundaries = clipStartBoundariesByComposition.get(localTimelineCompId || rootCompositionId || "") ?? [];
|
|
4968
5067
|
for (let i2 = 0; i2 < gsapWindows.length; i2++) {
|
|
4969
5068
|
const left = gsapWindows[i2];
|
|
4970
5069
|
if (!left) continue;
|
|
@@ -4993,6 +5092,25 @@ ${right.raw}`)
|
|
|
4993
5092
|
});
|
|
4994
5093
|
}
|
|
4995
5094
|
}
|
|
5095
|
+
if (clipStartBoundaries.length > 0) {
|
|
5096
|
+
for (const win of gsapWindows) {
|
|
5097
|
+
if (!isSceneBoundaryExit(win)) continue;
|
|
5098
|
+
const boundary = findMatchingSceneBoundary(win.end, clipStartBoundaries);
|
|
5099
|
+
if (boundary == null) continue;
|
|
5100
|
+
const hasHardKill = gsapWindows.some(
|
|
5101
|
+
(candidate) => isHardKillSet(candidate, win.targetSelector, boundary)
|
|
5102
|
+
);
|
|
5103
|
+
if (hasHardKill) continue;
|
|
5104
|
+
findings.push({
|
|
5105
|
+
code: "gsap_exit_missing_hard_kill",
|
|
5106
|
+
severity: "warning",
|
|
5107
|
+
message: `GSAP exit on "${win.targetSelector}" ends at the ${boundary.toFixed(2)}s clip start boundary without a matching tl.set hard kill. Non-linear seeking can land after the fade and leave stale visibility state.`,
|
|
5108
|
+
selector: win.targetSelector,
|
|
5109
|
+
fixHint: `Add \`tl.set("${win.targetSelector}", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\` after the exit tween.`,
|
|
5110
|
+
snippet: truncateSnippet(win.raw)
|
|
5111
|
+
});
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
4996
5114
|
for (const win of gsapWindows) {
|
|
4997
5115
|
const sel = win.targetSelector;
|
|
4998
5116
|
const clipInfo = clipIds.get(sel) || clipClasses.get(sel);
|
|
@@ -8231,22 +8349,22 @@ function getMeasureContext() {
|
|
|
8231
8349
|
throw new Error("Text measurement requires OffscreenCanvas or a DOM canvas context.");
|
|
8232
8350
|
}
|
|
8233
8351
|
function getSegmentMetricCache(font) {
|
|
8234
|
-
let
|
|
8235
|
-
if (!
|
|
8236
|
-
|
|
8237
|
-
segmentMetricCaches.set(font,
|
|
8352
|
+
let cache2 = segmentMetricCaches.get(font);
|
|
8353
|
+
if (!cache2) {
|
|
8354
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
8355
|
+
segmentMetricCaches.set(font, cache2);
|
|
8238
8356
|
}
|
|
8239
|
-
return
|
|
8357
|
+
return cache2;
|
|
8240
8358
|
}
|
|
8241
|
-
function getSegmentMetrics(seg,
|
|
8242
|
-
let metrics =
|
|
8359
|
+
function getSegmentMetrics(seg, cache2) {
|
|
8360
|
+
let metrics = cache2.get(seg);
|
|
8243
8361
|
if (metrics === void 0) {
|
|
8244
8362
|
const ctx = getMeasureContext();
|
|
8245
8363
|
metrics = {
|
|
8246
8364
|
width: ctx.measureText(seg).width,
|
|
8247
8365
|
containsCJK: isCJK(seg)
|
|
8248
8366
|
};
|
|
8249
|
-
|
|
8367
|
+
cache2.set(seg, metrics);
|
|
8250
8368
|
}
|
|
8251
8369
|
return metrics;
|
|
8252
8370
|
}
|
|
@@ -8335,7 +8453,7 @@ function getCorrectedSegmentWidth(seg, metrics, emojiCorrection) {
|
|
|
8335
8453
|
return metrics.width;
|
|
8336
8454
|
return metrics.width - getEmojiCount(seg, metrics) * emojiCorrection;
|
|
8337
8455
|
}
|
|
8338
|
-
function getSegmentBreakableFitAdvances(seg, metrics,
|
|
8456
|
+
function getSegmentBreakableFitAdvances(seg, metrics, cache2, emojiCorrection, mode) {
|
|
8339
8457
|
if (metrics.breakableFitAdvances !== void 0)
|
|
8340
8458
|
return metrics.breakableFitAdvances;
|
|
8341
8459
|
const graphemeSegmenter = getSharedGraphemeSegmenter();
|
|
@@ -8350,7 +8468,7 @@ function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mo
|
|
|
8350
8468
|
if (mode === "sum-graphemes") {
|
|
8351
8469
|
const advances2 = [];
|
|
8352
8470
|
for (const grapheme of graphemes) {
|
|
8353
|
-
const graphemeMetrics = getSegmentMetrics(grapheme,
|
|
8471
|
+
const graphemeMetrics = getSegmentMetrics(grapheme, cache2);
|
|
8354
8472
|
advances2.push(getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection));
|
|
8355
8473
|
}
|
|
8356
8474
|
metrics.breakableFitAdvances = advances2;
|
|
@@ -8361,13 +8479,13 @@ function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mo
|
|
|
8361
8479
|
let previousGrapheme = null;
|
|
8362
8480
|
let previousWidth = 0;
|
|
8363
8481
|
for (const grapheme of graphemes) {
|
|
8364
|
-
const graphemeMetrics = getSegmentMetrics(grapheme,
|
|
8482
|
+
const graphemeMetrics = getSegmentMetrics(grapheme, cache2);
|
|
8365
8483
|
const currentWidth = getCorrectedSegmentWidth(grapheme, graphemeMetrics, emojiCorrection);
|
|
8366
8484
|
if (previousGrapheme === null) {
|
|
8367
8485
|
advances2.push(currentWidth);
|
|
8368
8486
|
} else {
|
|
8369
8487
|
const pair = previousGrapheme + grapheme;
|
|
8370
|
-
const pairMetrics = getSegmentMetrics(pair,
|
|
8488
|
+
const pairMetrics = getSegmentMetrics(pair, cache2);
|
|
8371
8489
|
advances2.push(getCorrectedSegmentWidth(pair, pairMetrics, emojiCorrection) - previousWidth);
|
|
8372
8490
|
}
|
|
8373
8491
|
previousGrapheme = grapheme;
|
|
@@ -8381,7 +8499,7 @@ function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mo
|
|
|
8381
8499
|
let prefixWidth = 0;
|
|
8382
8500
|
for (const grapheme of graphemes) {
|
|
8383
8501
|
prefix += grapheme;
|
|
8384
|
-
const prefixMetrics = getSegmentMetrics(prefix,
|
|
8502
|
+
const prefixMetrics = getSegmentMetrics(prefix, cache2);
|
|
8385
8503
|
const nextPrefixWidth = getCorrectedSegmentWidth(prefix, prefixMetrics, emojiCorrection);
|
|
8386
8504
|
advances.push(nextPrefixWidth - prefixWidth);
|
|
8387
8505
|
prefixWidth = nextPrefixWidth;
|
|
@@ -8392,10 +8510,10 @@ function getSegmentBreakableFitAdvances(seg, metrics, cache, emojiCorrection, mo
|
|
|
8392
8510
|
function getFontMeasurementState(font, needsEmojiCorrection) {
|
|
8393
8511
|
const ctx = getMeasureContext();
|
|
8394
8512
|
ctx.font = font;
|
|
8395
|
-
const
|
|
8513
|
+
const cache2 = getSegmentMetricCache(font);
|
|
8396
8514
|
const fontSize = parseFontSize(font);
|
|
8397
8515
|
const emojiCorrection = needsEmojiCorrection ? getEmojiCorrection(font, fontSize) : 0;
|
|
8398
|
-
return { cache, fontSize, emojiCorrection };
|
|
8516
|
+
return { cache: cache2, fontSize, emojiCorrection };
|
|
8399
8517
|
}
|
|
8400
8518
|
var measureContext, segmentMetricCaches, cachedEngineProfile, MAX_PREFIX_FIT_GRAPHEMES, emojiPresentationRe, maybeEmojiRe, sharedGraphemeSegmenter, emojiCorrectionCache;
|
|
8401
8519
|
var init_measurement = __esm({
|
|
@@ -8939,9 +9057,9 @@ function mergeKeepAllTextUnits(units) {
|
|
|
8939
9057
|
}
|
|
8940
9058
|
function measureAnalysis(analysis, font, includeSegments, wordBreak) {
|
|
8941
9059
|
const engineProfile = getEngineProfile();
|
|
8942
|
-
const { cache, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
|
|
8943
|
-
const discretionaryHyphenWidth = getCorrectedSegmentWidth("-", getSegmentMetrics("-",
|
|
8944
|
-
const spaceWidth = getCorrectedSegmentWidth(" ", getSegmentMetrics(" ",
|
|
9060
|
+
const { cache: cache2, emojiCorrection } = getFontMeasurementState(font, textMayContainEmoji(analysis.normalized));
|
|
9061
|
+
const discretionaryHyphenWidth = getCorrectedSegmentWidth("-", getSegmentMetrics("-", cache2), emojiCorrection);
|
|
9062
|
+
const spaceWidth = getCorrectedSegmentWidth(" ", getSegmentMetrics(" ", cache2), emojiCorrection);
|
|
8945
9063
|
const tabStopAdvance = spaceWidth * 8;
|
|
8946
9064
|
if (analysis.len === 0)
|
|
8947
9065
|
return createEmptyPrepared(includeSegments);
|
|
@@ -8968,7 +9086,7 @@ function measureAnalysis(analysis, font, includeSegments, wordBreak) {
|
|
|
8968
9086
|
segments.push(text);
|
|
8969
9087
|
}
|
|
8970
9088
|
function pushMeasuredTextSegment(text, kind, start, wordLike, allowOverflowBreaks) {
|
|
8971
|
-
const textMetrics = getSegmentMetrics(text,
|
|
9089
|
+
const textMetrics = getSegmentMetrics(text, cache2);
|
|
8972
9090
|
const width = getCorrectedSegmentWidth(text, textMetrics, emojiCorrection);
|
|
8973
9091
|
const lineEndFitAdvance = kind === "space" || kind === "preserved-space" || kind === "zero-width-break" ? 0 : width;
|
|
8974
9092
|
const lineEndPaintAdvance = kind === "space" || kind === "zero-width-break" ? 0 : width;
|
|
@@ -8979,7 +9097,7 @@ function measureAnalysis(analysis, font, includeSegments, wordBreak) {
|
|
|
8979
9097
|
} else if (engineProfile.preferPrefixWidthsForBreakableRuns) {
|
|
8980
9098
|
fitMode = "segment-prefixes";
|
|
8981
9099
|
}
|
|
8982
|
-
const fitAdvances = getSegmentBreakableFitAdvances(text, textMetrics,
|
|
9100
|
+
const fitAdvances = getSegmentBreakableFitAdvances(text, textMetrics, cache2, emojiCorrection, fitMode);
|
|
8983
9101
|
pushMeasuredSegment(text, width, lineEndFitAdvance, lineEndPaintAdvance, kind, start, fitAdvances);
|
|
8984
9102
|
return;
|
|
8985
9103
|
}
|
|
@@ -9003,7 +9121,7 @@ function measureAnalysis(analysis, font, includeSegments, wordBreak) {
|
|
|
9003
9121
|
pushMeasuredSegment(segText, 0, 0, 0, segKind, segStart, null);
|
|
9004
9122
|
continue;
|
|
9005
9123
|
}
|
|
9006
|
-
const segMetrics = getSegmentMetrics(segText,
|
|
9124
|
+
const segMetrics = getSegmentMetrics(segText, cache2);
|
|
9007
9125
|
if (segKind === "text" && segMetrics.containsCJK) {
|
|
9008
9126
|
const baseUnits = buildBaseCjkUnits(segText, engineProfile);
|
|
9009
9127
|
const measuredUnits = wordBreak === "keep-all" ? mergeKeepAllTextUnits(baseUnits) : baseUnits;
|
|
@@ -9801,7 +9919,24 @@ function trackRenderComplete(props) {
|
|
|
9801
9919
|
capture_avg_ms: props.captureAvgMs,
|
|
9802
9920
|
capture_peak_ms: props.capturePeakMs,
|
|
9803
9921
|
peak_memory_mb: props.peakMemoryMb,
|
|
9804
|
-
memory_free_mb: props.memoryFreeMb
|
|
9922
|
+
memory_free_mb: props.memoryFreeMb,
|
|
9923
|
+
tmp_peak_bytes: props.tmpPeakBytes,
|
|
9924
|
+
stage_compile_ms: props.stageCompileMs,
|
|
9925
|
+
stage_video_extract_ms: props.stageVideoExtractMs,
|
|
9926
|
+
stage_audio_process_ms: props.stageAudioProcessMs,
|
|
9927
|
+
stage_capture_ms: props.stageCaptureMs,
|
|
9928
|
+
stage_encode_ms: props.stageEncodeMs,
|
|
9929
|
+
stage_assemble_ms: props.stageAssembleMs,
|
|
9930
|
+
extract_resolve_ms: props.extractResolveMs,
|
|
9931
|
+
extract_hdr_probe_ms: props.extractHdrProbeMs,
|
|
9932
|
+
extract_hdr_preflight_ms: props.extractHdrPreflightMs,
|
|
9933
|
+
extract_hdr_preflight_count: props.extractHdrPreflightCount,
|
|
9934
|
+
extract_vfr_probe_ms: props.extractVfrProbeMs,
|
|
9935
|
+
extract_vfr_preflight_ms: props.extractVfrPreflightMs,
|
|
9936
|
+
extract_vfr_preflight_count: props.extractVfrPreflightCount,
|
|
9937
|
+
extract_phase3_ms: props.extractPhase3Ms,
|
|
9938
|
+
extract_cache_hits: props.extractCacheHits,
|
|
9939
|
+
extract_cache_misses: props.extractCacheMisses
|
|
9805
9940
|
});
|
|
9806
9941
|
}
|
|
9807
9942
|
function trackRenderError(props) {
|
|
@@ -24970,7 +25105,8 @@ function resolveConfig(overrides) {
|
|
|
24970
25105
|
DEFAULT_CONFIG2.renderReadyTimeout
|
|
24971
25106
|
),
|
|
24972
25107
|
verifyRuntime: env("PRODUCER_VERIFY_HYPERFRAME_RUNTIME") !== "false",
|
|
24973
|
-
runtimeManifestPath: env("PRODUCER_HYPERFRAME_MANIFEST_PATH")
|
|
25108
|
+
runtimeManifestPath: env("PRODUCER_HYPERFRAME_MANIFEST_PATH"),
|
|
25109
|
+
extractCacheDir: env("HYPERFRAMES_EXTRACT_CACHE_DIR")
|
|
24974
25110
|
};
|
|
24975
25111
|
const cleanEnv = Object.fromEntries(Object.entries(fromEnv).filter(([, v]) => v !== void 0));
|
|
24976
25112
|
return {
|
|
@@ -27127,13 +27263,130 @@ var init_urlDownloader = __esm({
|
|
|
27127
27263
|
}
|
|
27128
27264
|
});
|
|
27129
27265
|
|
|
27266
|
+
// ../engine/src/utils/htmlTemplate.ts
|
|
27267
|
+
function parseHTMLContent(html) {
|
|
27268
|
+
const trimmed = html.trimStart().toLowerCase();
|
|
27269
|
+
if (trimmed.startsWith("<!doctype") || trimmed.startsWith("<html")) {
|
|
27270
|
+
return parseHTML(html).document;
|
|
27271
|
+
}
|
|
27272
|
+
return parseHTML(`<!DOCTYPE html><html><head></head><body>${html}</body></html>`).document;
|
|
27273
|
+
}
|
|
27274
|
+
function getSingleMeaningfulChild(container) {
|
|
27275
|
+
let child = null;
|
|
27276
|
+
for (const node of Array.from(container.childNodes)) {
|
|
27277
|
+
if (node.nodeType === 3 && !(node.textContent || "").trim()) continue;
|
|
27278
|
+
if (node.nodeType === 8) continue;
|
|
27279
|
+
if (node.nodeType !== 1) return null;
|
|
27280
|
+
if (child) return null;
|
|
27281
|
+
child = node;
|
|
27282
|
+
}
|
|
27283
|
+
return child;
|
|
27284
|
+
}
|
|
27285
|
+
function unwrapTemplate(html) {
|
|
27286
|
+
const lowered = html.toLowerCase();
|
|
27287
|
+
if (!lowered.includes("<template") || !lowered.includes("</template>")) {
|
|
27288
|
+
return html;
|
|
27289
|
+
}
|
|
27290
|
+
const { body } = parseHTMLContent(html);
|
|
27291
|
+
if (!body) return html;
|
|
27292
|
+
let container = body;
|
|
27293
|
+
const bodyWrapper = getSingleMeaningfulChild(container);
|
|
27294
|
+
if (bodyWrapper?.tagName === "BODY") {
|
|
27295
|
+
container = bodyWrapper;
|
|
27296
|
+
}
|
|
27297
|
+
const template = getSingleMeaningfulChild(container);
|
|
27298
|
+
if (template?.tagName !== "TEMPLATE") {
|
|
27299
|
+
return html;
|
|
27300
|
+
}
|
|
27301
|
+
return template.innerHTML ?? html;
|
|
27302
|
+
}
|
|
27303
|
+
var init_htmlTemplate = __esm({
|
|
27304
|
+
"../engine/src/utils/htmlTemplate.ts"() {
|
|
27305
|
+
"use strict";
|
|
27306
|
+
init_esm10();
|
|
27307
|
+
}
|
|
27308
|
+
});
|
|
27309
|
+
|
|
27310
|
+
// ../engine/src/services/extractionCache.ts
|
|
27311
|
+
import { createHash as createHash2 } from "crypto";
|
|
27312
|
+
import { mkdirSync as mkdirSync12, readdirSync as readdirSync8, statSync as statSync6, writeFileSync as writeFileSync10 } from "fs";
|
|
27313
|
+
import { existsSync as existsSync20 } from "fs";
|
|
27314
|
+
import { join as join22 } from "path";
|
|
27315
|
+
function readKeyStat(videoPath) {
|
|
27316
|
+
try {
|
|
27317
|
+
const stat3 = statSync6(videoPath);
|
|
27318
|
+
return { mtimeMs: Math.floor(stat3.mtimeMs), size: stat3.size };
|
|
27319
|
+
} catch {
|
|
27320
|
+
return null;
|
|
27321
|
+
}
|
|
27322
|
+
}
|
|
27323
|
+
function canonicalKeyBlob(input) {
|
|
27324
|
+
const durationForKey = Number.isFinite(input.duration) ? input.duration : -1;
|
|
27325
|
+
return JSON.stringify({
|
|
27326
|
+
p: input.videoPath,
|
|
27327
|
+
m: input.mtimeMs,
|
|
27328
|
+
s: input.size,
|
|
27329
|
+
ms: input.mediaStart,
|
|
27330
|
+
d: durationForKey,
|
|
27331
|
+
f: input.fps,
|
|
27332
|
+
fmt: input.format
|
|
27333
|
+
});
|
|
27334
|
+
}
|
|
27335
|
+
function computeCacheKey(input) {
|
|
27336
|
+
return createHash2("sha256").update(canonicalKeyBlob(input)).digest("hex");
|
|
27337
|
+
}
|
|
27338
|
+
function cacheEntryDirName(keyHash) {
|
|
27339
|
+
return SCHEMA_PREFIX + keyHash.slice(0, KEY_HEX_CHARS);
|
|
27340
|
+
}
|
|
27341
|
+
function lookupCacheEntry(rootDir, input) {
|
|
27342
|
+
const keyHash = computeCacheKey(input);
|
|
27343
|
+
const dir = join22(rootDir, cacheEntryDirName(keyHash));
|
|
27344
|
+
const complete = existsSync20(join22(dir, COMPLETE_SENTINEL));
|
|
27345
|
+
return { entry: { dir, keyHash }, hit: complete };
|
|
27346
|
+
}
|
|
27347
|
+
function ensureCacheEntryDir(entry) {
|
|
27348
|
+
mkdirSync12(entry.dir, { recursive: true });
|
|
27349
|
+
}
|
|
27350
|
+
function markCacheEntryComplete(entry) {
|
|
27351
|
+
writeFileSync10(join22(entry.dir, COMPLETE_SENTINEL), "", "utf-8");
|
|
27352
|
+
}
|
|
27353
|
+
function rehydrateCacheEntry(entry, options) {
|
|
27354
|
+
const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${options.format}`;
|
|
27355
|
+
const framePaths = /* @__PURE__ */ new Map();
|
|
27356
|
+
const suffix = `.${options.format}`;
|
|
27357
|
+
const files = readdirSync8(entry.dir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(suffix)).sort();
|
|
27358
|
+
files.forEach((file, idx) => {
|
|
27359
|
+
framePaths.set(idx, join22(entry.dir, file));
|
|
27360
|
+
});
|
|
27361
|
+
return {
|
|
27362
|
+
videoId: options.videoId,
|
|
27363
|
+
srcPath: options.srcPath,
|
|
27364
|
+
outputDir: entry.dir,
|
|
27365
|
+
framePattern,
|
|
27366
|
+
fps: options.fps,
|
|
27367
|
+
totalFrames: framePaths.size,
|
|
27368
|
+
metadata: options.metadata,
|
|
27369
|
+
framePaths
|
|
27370
|
+
};
|
|
27371
|
+
}
|
|
27372
|
+
var FRAME_FILENAME_PREFIX, COMPLETE_SENTINEL, SCHEMA_PREFIX, KEY_HEX_CHARS;
|
|
27373
|
+
var init_extractionCache = __esm({
|
|
27374
|
+
"../engine/src/services/extractionCache.ts"() {
|
|
27375
|
+
"use strict";
|
|
27376
|
+
FRAME_FILENAME_PREFIX = "frame_";
|
|
27377
|
+
COMPLETE_SENTINEL = ".hf-complete";
|
|
27378
|
+
SCHEMA_PREFIX = "hfcache-v2-";
|
|
27379
|
+
KEY_HEX_CHARS = 16;
|
|
27380
|
+
}
|
|
27381
|
+
});
|
|
27382
|
+
|
|
27130
27383
|
// ../engine/src/services/videoFrameExtractor.ts
|
|
27131
27384
|
import { spawn as spawn7 } from "child_process";
|
|
27132
|
-
import { existsSync as
|
|
27133
|
-
import { isAbsolute as isAbsolute2, join as
|
|
27385
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync13, readdirSync as readdirSync9, rmSync as rmSync5 } from "fs";
|
|
27386
|
+
import { isAbsolute as isAbsolute2, join as join23 } from "path";
|
|
27134
27387
|
function parseVideoElements(html) {
|
|
27135
27388
|
const videos = [];
|
|
27136
|
-
const { document: document2 } = parseHTML(html);
|
|
27389
|
+
const { document: document2 } = parseHTML(unwrapTemplate(html));
|
|
27137
27390
|
const videoEls = document2.querySelectorAll("video[src]");
|
|
27138
27391
|
let autoIdCounter = 0;
|
|
27139
27392
|
for (const el of videoEls) {
|
|
@@ -27170,7 +27423,7 @@ function parseVideoElements(html) {
|
|
|
27170
27423
|
}
|
|
27171
27424
|
function parseImageElements(html) {
|
|
27172
27425
|
const images = [];
|
|
27173
|
-
const { document: document2 } = parseHTML(html);
|
|
27426
|
+
const { document: document2 } = parseHTML(unwrapTemplate(html));
|
|
27174
27427
|
const imgEls = document2.querySelectorAll("img[src]");
|
|
27175
27428
|
let autoIdCounter = 0;
|
|
27176
27429
|
for (const el of imgEls) {
|
|
@@ -27196,14 +27449,14 @@ function parseImageElements(html) {
|
|
|
27196
27449
|
}
|
|
27197
27450
|
return images;
|
|
27198
27451
|
}
|
|
27199
|
-
async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config) {
|
|
27452
|
+
async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
|
|
27200
27453
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
27201
27454
|
const { fps, outputDir, quality = 95, format = "jpg" } = options;
|
|
27202
|
-
const videoOutputDir =
|
|
27203
|
-
if (!
|
|
27455
|
+
const videoOutputDir = outputDirOverride ?? join23(outputDir, videoId);
|
|
27456
|
+
if (!existsSync21(videoOutputDir)) mkdirSync13(videoOutputDir, { recursive: true });
|
|
27204
27457
|
const metadata = await extractMediaMetadata(videoPath);
|
|
27205
|
-
const framePattern =
|
|
27206
|
-
const outputPattern =
|
|
27458
|
+
const framePattern = `${FRAME_FILENAME_PREFIX}%05d.${format}`;
|
|
27459
|
+
const outputPattern = join23(videoOutputDir, framePattern);
|
|
27207
27460
|
const isHdr = isHdrColorSpace(metadata.colorSpace);
|
|
27208
27461
|
const isMacOS = process.platform === "darwin";
|
|
27209
27462
|
const args = [];
|
|
@@ -27251,9 +27504,9 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
27251
27504
|
return;
|
|
27252
27505
|
}
|
|
27253
27506
|
const framePaths = /* @__PURE__ */ new Map();
|
|
27254
|
-
const files =
|
|
27507
|
+
const files = readdirSync9(videoOutputDir).filter((f3) => f3.startsWith(FRAME_FILENAME_PREFIX) && f3.endsWith(`.${format}`)).sort();
|
|
27255
27508
|
files.forEach((file, index) => {
|
|
27256
|
-
framePaths.set(index,
|
|
27509
|
+
framePaths.set(index, join23(videoOutputDir, file));
|
|
27257
27510
|
});
|
|
27258
27511
|
resolve38({
|
|
27259
27512
|
videoId,
|
|
@@ -27277,12 +27530,19 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
|
|
|
27277
27530
|
});
|
|
27278
27531
|
});
|
|
27279
27532
|
}
|
|
27280
|
-
async function convertSdrToHdr(inputPath, outputPath, targetTransfer, signal, config) {
|
|
27533
|
+
async function convertSdrToHdr(inputPath, outputPath, startTime, duration, targetTransfer, signal, config) {
|
|
27534
|
+
if (duration <= 0) {
|
|
27535
|
+
throw new Error(`convertSdrToHdr: duration must be positive (got ${duration})`);
|
|
27536
|
+
}
|
|
27281
27537
|
const timeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
27282
27538
|
const colorTrc = targetTransfer === "pq" ? "smpte2084" : "arib-std-b67";
|
|
27283
27539
|
const args = [
|
|
27540
|
+
"-ss",
|
|
27541
|
+
String(startTime),
|
|
27284
27542
|
"-i",
|
|
27285
27543
|
inputPath,
|
|
27544
|
+
"-t",
|
|
27545
|
+
String(duration),
|
|
27286
27546
|
"-vf",
|
|
27287
27547
|
"colorspace=all=bt2020:iall=bt709:range=tv",
|
|
27288
27548
|
"-color_primaries",
|
|
@@ -27309,6 +27569,11 @@ async function convertSdrToHdr(inputPath, outputPath, targetTransfer, signal, co
|
|
|
27309
27569
|
);
|
|
27310
27570
|
}
|
|
27311
27571
|
}
|
|
27572
|
+
function resolveSegmentDuration(requested, mediaStart, metadata) {
|
|
27573
|
+
if (Number.isFinite(requested) && requested > 0) return requested;
|
|
27574
|
+
const sourceRemaining = metadata.durationSeconds - mediaStart;
|
|
27575
|
+
return sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
|
|
27576
|
+
}
|
|
27312
27577
|
async function convertVfrToCfr(inputPath, outputPath, targetFps, startTime, duration, signal, config) {
|
|
27313
27578
|
const timeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
27314
27579
|
const args = [
|
|
@@ -27345,21 +27610,34 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27345
27610
|
const extracted = [];
|
|
27346
27611
|
const errors = [];
|
|
27347
27612
|
let totalFramesExtracted = 0;
|
|
27613
|
+
const breakdown = {
|
|
27614
|
+
resolveMs: 0,
|
|
27615
|
+
hdrProbeMs: 0,
|
|
27616
|
+
hdrPreflightMs: 0,
|
|
27617
|
+
hdrPreflightCount: 0,
|
|
27618
|
+
vfrProbeMs: 0,
|
|
27619
|
+
vfrPreflightMs: 0,
|
|
27620
|
+
vfrPreflightCount: 0,
|
|
27621
|
+
extractMs: 0,
|
|
27622
|
+
cacheHits: 0,
|
|
27623
|
+
cacheMisses: 0
|
|
27624
|
+
};
|
|
27625
|
+
const phase1Start = Date.now();
|
|
27348
27626
|
const resolvedVideos = [];
|
|
27349
27627
|
for (const video of videos) {
|
|
27350
27628
|
if (signal?.aborted) break;
|
|
27351
27629
|
try {
|
|
27352
27630
|
let videoPath = video.src;
|
|
27353
27631
|
if (!isAbsolute2(videoPath) && !isHttpUrl(videoPath)) {
|
|
27354
|
-
const fromCompiled = compiledDir ?
|
|
27355
|
-
videoPath = fromCompiled &&
|
|
27632
|
+
const fromCompiled = compiledDir ? join23(compiledDir, videoPath) : null;
|
|
27633
|
+
videoPath = fromCompiled && existsSync21(fromCompiled) ? fromCompiled : join23(baseDir, videoPath);
|
|
27356
27634
|
}
|
|
27357
27635
|
if (isHttpUrl(videoPath)) {
|
|
27358
|
-
const downloadDir =
|
|
27359
|
-
|
|
27636
|
+
const downloadDir = join23(options.outputDir, "_downloads");
|
|
27637
|
+
mkdirSync13(downloadDir, { recursive: true });
|
|
27360
27638
|
videoPath = await downloadToTemp(videoPath, downloadDir);
|
|
27361
27639
|
}
|
|
27362
|
-
if (!
|
|
27640
|
+
if (!existsSync21(videoPath)) {
|
|
27363
27641
|
errors.push({ videoId: video.id, error: `Video file not found: ${videoPath}` });
|
|
27364
27642
|
continue;
|
|
27365
27643
|
}
|
|
@@ -27368,27 +27646,66 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27368
27646
|
errors.push({ videoId: video.id, error: err instanceof Error ? err.message : String(err) });
|
|
27369
27647
|
}
|
|
27370
27648
|
}
|
|
27371
|
-
|
|
27372
|
-
|
|
27373
|
-
|
|
27374
|
-
|
|
27375
|
-
|
|
27649
|
+
breakdown.resolveMs = Date.now() - phase1Start;
|
|
27650
|
+
const cacheKeyInputs = resolvedVideos.map(({ video, videoPath }) => {
|
|
27651
|
+
const stat3 = readKeyStat(videoPath);
|
|
27652
|
+
if (!stat3) return null;
|
|
27653
|
+
return {
|
|
27654
|
+
videoPath,
|
|
27655
|
+
mtimeMs: stat3.mtimeMs,
|
|
27656
|
+
size: stat3.size,
|
|
27657
|
+
mediaStart: video.mediaStart,
|
|
27658
|
+
start: video.start,
|
|
27659
|
+
end: video.end
|
|
27660
|
+
};
|
|
27661
|
+
});
|
|
27662
|
+
const phase2ProbeStart = Date.now();
|
|
27663
|
+
const videoMetadata = await Promise.all(
|
|
27664
|
+
resolvedVideos.map(({ videoPath }) => extractMediaMetadata(videoPath))
|
|
27376
27665
|
);
|
|
27666
|
+
const videoColorSpaces = videoMetadata.map((m2) => m2.colorSpace);
|
|
27667
|
+
breakdown.hdrProbeMs = Date.now() - phase2ProbeStart;
|
|
27668
|
+
const hdrPreflightStart = Date.now();
|
|
27377
27669
|
const hdrInfo = analyzeCompositionHdr(videoColorSpaces);
|
|
27670
|
+
const hdrSkippedIndices = /* @__PURE__ */ new Set();
|
|
27378
27671
|
if (hdrInfo.hasHdr && hdrInfo.dominantTransfer) {
|
|
27379
27672
|
const targetTransfer = hdrInfo.dominantTransfer;
|
|
27380
|
-
const convertDir =
|
|
27381
|
-
|
|
27673
|
+
const convertDir = join23(options.outputDir, "_hdr_normalized");
|
|
27674
|
+
mkdirSync13(convertDir, { recursive: true });
|
|
27382
27675
|
for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
|
|
27383
27676
|
if (signal?.aborted) break;
|
|
27384
27677
|
const cs = videoColorSpaces[i2] ?? null;
|
|
27385
27678
|
if (!isHdrColorSpace(cs)) {
|
|
27386
27679
|
const entry = resolvedVideos[i2];
|
|
27387
|
-
|
|
27388
|
-
|
|
27680
|
+
const metadata = videoMetadata[i2];
|
|
27681
|
+
if (!entry || !metadata) continue;
|
|
27682
|
+
if (entry.video.mediaStart >= metadata.durationSeconds) {
|
|
27683
|
+
errors.push({
|
|
27684
|
+
videoId: entry.video.id,
|
|
27685
|
+
error: `SDR\u2192HDR conversion skipped: mediaStart (${entry.video.mediaStart}s) \u2265 source duration (${metadata.durationSeconds}s)`
|
|
27686
|
+
});
|
|
27687
|
+
hdrSkippedIndices.add(i2);
|
|
27688
|
+
continue;
|
|
27689
|
+
}
|
|
27690
|
+
let segDuration = entry.video.end - entry.video.start;
|
|
27691
|
+
if (!Number.isFinite(segDuration) || segDuration <= 0) {
|
|
27692
|
+
const sourceRemaining = metadata.durationSeconds - entry.video.mediaStart;
|
|
27693
|
+
segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
|
|
27694
|
+
}
|
|
27695
|
+
const convertedPath = join23(convertDir, `${entry.video.id}_hdr.mp4`);
|
|
27389
27696
|
try {
|
|
27390
|
-
await convertSdrToHdr(
|
|
27697
|
+
await convertSdrToHdr(
|
|
27698
|
+
entry.videoPath,
|
|
27699
|
+
convertedPath,
|
|
27700
|
+
entry.video.mediaStart,
|
|
27701
|
+
segDuration,
|
|
27702
|
+
targetTransfer,
|
|
27703
|
+
signal,
|
|
27704
|
+
config
|
|
27705
|
+
);
|
|
27391
27706
|
entry.videoPath = convertedPath;
|
|
27707
|
+
entry.video = { ...entry.video, mediaStart: 0 };
|
|
27708
|
+
breakdown.hdrPreflightCount += 1;
|
|
27392
27709
|
} catch (err) {
|
|
27393
27710
|
errors.push({
|
|
27394
27711
|
videoId: entry.video.id,
|
|
@@ -27398,20 +27715,34 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27398
27715
|
}
|
|
27399
27716
|
}
|
|
27400
27717
|
}
|
|
27401
|
-
|
|
27718
|
+
breakdown.hdrPreflightMs = Date.now() - hdrPreflightStart;
|
|
27719
|
+
if (hdrSkippedIndices.size > 0) {
|
|
27720
|
+
for (let i2 = resolvedVideos.length - 1; i2 >= 0; i2--) {
|
|
27721
|
+
if (hdrSkippedIndices.has(i2)) {
|
|
27722
|
+
resolvedVideos.splice(i2, 1);
|
|
27723
|
+
videoMetadata.splice(i2, 1);
|
|
27724
|
+
videoColorSpaces.splice(i2, 1);
|
|
27725
|
+
cacheKeyInputs.splice(i2, 1);
|
|
27726
|
+
}
|
|
27727
|
+
}
|
|
27728
|
+
}
|
|
27729
|
+
const vfrPreflightStart = Date.now();
|
|
27730
|
+
const vfrNormDir = join23(options.outputDir, "_vfr_normalized");
|
|
27402
27731
|
for (let i2 = 0; i2 < resolvedVideos.length; i2++) {
|
|
27403
27732
|
if (signal?.aborted) break;
|
|
27404
27733
|
const entry = resolvedVideos[i2];
|
|
27405
27734
|
if (!entry) continue;
|
|
27735
|
+
const vfrProbeStart = Date.now();
|
|
27406
27736
|
const metadata = await extractMediaMetadata(entry.videoPath);
|
|
27737
|
+
breakdown.vfrProbeMs += Date.now() - vfrProbeStart;
|
|
27407
27738
|
if (!metadata.isVFR) continue;
|
|
27408
27739
|
let segDuration = entry.video.end - entry.video.start;
|
|
27409
27740
|
if (!Number.isFinite(segDuration) || segDuration <= 0) {
|
|
27410
27741
|
const sourceRemaining = metadata.durationSeconds - entry.video.mediaStart;
|
|
27411
27742
|
segDuration = sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
|
|
27412
27743
|
}
|
|
27413
|
-
|
|
27414
|
-
const normalizedPath =
|
|
27744
|
+
mkdirSync13(vfrNormDir, { recursive: true });
|
|
27745
|
+
const normalizedPath = join23(vfrNormDir, `${entry.video.id}_cfr.mp4`);
|
|
27415
27746
|
try {
|
|
27416
27747
|
await convertVfrToCfr(
|
|
27417
27748
|
entry.videoPath,
|
|
@@ -27424,6 +27755,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27424
27755
|
);
|
|
27425
27756
|
entry.videoPath = normalizedPath;
|
|
27426
27757
|
entry.video = { ...entry.video, mediaStart: 0 };
|
|
27758
|
+
breakdown.vfrPreflightCount += 1;
|
|
27427
27759
|
} catch (err) {
|
|
27428
27760
|
errors.push({
|
|
27429
27761
|
videoId: entry.video.id,
|
|
@@ -27431,19 +27763,72 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27431
27763
|
});
|
|
27432
27764
|
}
|
|
27433
27765
|
}
|
|
27766
|
+
breakdown.vfrPreflightMs = Date.now() - vfrPreflightStart;
|
|
27767
|
+
const phase3Start = Date.now();
|
|
27768
|
+
const cacheRootDir = config?.extractCacheDir;
|
|
27769
|
+
const cacheFormat = options.format ?? "jpg";
|
|
27770
|
+
async function tryCachedExtract(video, videoPath, videoDuration, i2) {
|
|
27771
|
+
if (!cacheRootDir) return null;
|
|
27772
|
+
const keyInput = cacheKeyInputs[i2];
|
|
27773
|
+
const probedMeta = videoMetadata[i2];
|
|
27774
|
+
if (!keyInput || !probedMeta) return null;
|
|
27775
|
+
const keyDuration = resolveSegmentDuration(
|
|
27776
|
+
keyInput.end - keyInput.start,
|
|
27777
|
+
keyInput.mediaStart,
|
|
27778
|
+
probedMeta
|
|
27779
|
+
);
|
|
27780
|
+
const lookup = lookupCacheEntry(cacheRootDir, {
|
|
27781
|
+
videoPath: keyInput.videoPath,
|
|
27782
|
+
mtimeMs: keyInput.mtimeMs,
|
|
27783
|
+
size: keyInput.size,
|
|
27784
|
+
mediaStart: keyInput.mediaStart,
|
|
27785
|
+
duration: keyDuration,
|
|
27786
|
+
fps: options.fps,
|
|
27787
|
+
format: cacheFormat
|
|
27788
|
+
});
|
|
27789
|
+
if (lookup.hit) {
|
|
27790
|
+
breakdown.cacheHits += 1;
|
|
27791
|
+
const rehydrated = rehydrateCacheEntry(lookup.entry, {
|
|
27792
|
+
videoId: video.id,
|
|
27793
|
+
srcPath: keyInput.videoPath,
|
|
27794
|
+
fps: options.fps,
|
|
27795
|
+
format: cacheFormat,
|
|
27796
|
+
metadata: probedMeta
|
|
27797
|
+
});
|
|
27798
|
+
return { ...rehydrated, ownedByLookup: true };
|
|
27799
|
+
}
|
|
27800
|
+
breakdown.cacheMisses += 1;
|
|
27801
|
+
ensureCacheEntryDir(lookup.entry);
|
|
27802
|
+
const result = await extractVideoFramesRange(
|
|
27803
|
+
videoPath,
|
|
27804
|
+
video.id,
|
|
27805
|
+
video.mediaStart,
|
|
27806
|
+
videoDuration,
|
|
27807
|
+
options,
|
|
27808
|
+
signal,
|
|
27809
|
+
config,
|
|
27810
|
+
lookup.entry.dir
|
|
27811
|
+
);
|
|
27812
|
+
markCacheEntryComplete(lookup.entry);
|
|
27813
|
+
return { ...result, ownedByLookup: true };
|
|
27814
|
+
}
|
|
27434
27815
|
const results = await Promise.all(
|
|
27435
|
-
resolvedVideos.map(async ({ video, videoPath }) => {
|
|
27816
|
+
resolvedVideos.map(async ({ video, videoPath }, i2) => {
|
|
27436
27817
|
if (signal?.aborted) {
|
|
27437
27818
|
throw new Error("Video frame extraction cancelled");
|
|
27438
27819
|
}
|
|
27439
27820
|
try {
|
|
27440
|
-
|
|
27441
|
-
|
|
27442
|
-
|
|
27443
|
-
|
|
27444
|
-
|
|
27821
|
+
const probedMeta = videoMetadata[i2] ?? await extractMediaMetadata(videoPath);
|
|
27822
|
+
const videoDuration = resolveSegmentDuration(
|
|
27823
|
+
video.end - video.start,
|
|
27824
|
+
video.mediaStart,
|
|
27825
|
+
probedMeta
|
|
27826
|
+
);
|
|
27827
|
+
if (video.end - video.start !== videoDuration) {
|
|
27445
27828
|
video.end = video.start + videoDuration;
|
|
27446
27829
|
}
|
|
27830
|
+
const cached2 = await tryCachedExtract(video, videoPath, videoDuration, i2);
|
|
27831
|
+
if (cached2) return { result: cached2 };
|
|
27447
27832
|
const result = await extractVideoFramesRange(
|
|
27448
27833
|
videoPath,
|
|
27449
27834
|
video.id,
|
|
@@ -27464,6 +27849,7 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27464
27849
|
}
|
|
27465
27850
|
})
|
|
27466
27851
|
);
|
|
27852
|
+
breakdown.extractMs = Date.now() - phase3Start;
|
|
27467
27853
|
for (const item of results) {
|
|
27468
27854
|
if ("error" in item && item.error) {
|
|
27469
27855
|
errors.push(item.error);
|
|
@@ -27477,7 +27863,8 @@ async function extractAllVideoFrames(videos, baseDir, options, signal, config, c
|
|
|
27477
27863
|
extracted,
|
|
27478
27864
|
errors,
|
|
27479
27865
|
totalFramesExtracted,
|
|
27480
|
-
durationMs: Date.now() - startTime
|
|
27866
|
+
durationMs: Date.now() - startTime,
|
|
27867
|
+
phaseBreakdown: breakdown
|
|
27481
27868
|
};
|
|
27482
27869
|
}
|
|
27483
27870
|
function getFrameAtTime(extracted, globalTime, videoStart) {
|
|
@@ -27507,6 +27894,8 @@ var init_videoFrameExtractor = __esm({
|
|
|
27507
27894
|
init_urlDownloader();
|
|
27508
27895
|
init_runFfmpeg();
|
|
27509
27896
|
init_config2();
|
|
27897
|
+
init_htmlTemplate();
|
|
27898
|
+
init_extractionCache();
|
|
27510
27899
|
FrameLookupTable = class {
|
|
27511
27900
|
videos = /* @__PURE__ */ new Map();
|
|
27512
27901
|
orderedVideos = [];
|
|
@@ -27590,7 +27979,8 @@ var init_videoFrameExtractor = __esm({
|
|
|
27590
27979
|
}
|
|
27591
27980
|
cleanup() {
|
|
27592
27981
|
for (const video of this.videos.values()) {
|
|
27593
|
-
if (
|
|
27982
|
+
if (video.extracted.ownedByLookup) continue;
|
|
27983
|
+
if (existsSync21(video.extracted.outputDir)) {
|
|
27594
27984
|
rmSync5(video.extracted.outputDir, { recursive: true, force: true });
|
|
27595
27985
|
}
|
|
27596
27986
|
}
|
|
@@ -27605,23 +27995,23 @@ var init_videoFrameExtractor = __esm({
|
|
|
27605
27995
|
// ../engine/src/services/videoFrameInjector.ts
|
|
27606
27996
|
import { promises as fs } from "fs";
|
|
27607
27997
|
function createFrameDataUriCache(cacheLimit) {
|
|
27608
|
-
const
|
|
27998
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
27609
27999
|
const inFlight = /* @__PURE__ */ new Map();
|
|
27610
28000
|
function remember(framePath, dataUri) {
|
|
27611
|
-
if (
|
|
27612
|
-
|
|
28001
|
+
if (cache2.has(framePath)) {
|
|
28002
|
+
cache2.delete(framePath);
|
|
27613
28003
|
}
|
|
27614
|
-
|
|
27615
|
-
if (
|
|
27616
|
-
const oldestKey =
|
|
28004
|
+
cache2.set(framePath, dataUri);
|
|
28005
|
+
if (cache2.size > cacheLimit) {
|
|
28006
|
+
const oldestKey = cache2.keys().next().value;
|
|
27617
28007
|
if (oldestKey) {
|
|
27618
|
-
|
|
28008
|
+
cache2.delete(oldestKey);
|
|
27619
28009
|
}
|
|
27620
28010
|
}
|
|
27621
28011
|
return dataUri;
|
|
27622
28012
|
}
|
|
27623
28013
|
async function get(framePath) {
|
|
27624
|
-
const cached2 =
|
|
28014
|
+
const cached2 = cache2.get(framePath);
|
|
27625
28015
|
if (cached2) {
|
|
27626
28016
|
remember(framePath, cached2);
|
|
27627
28017
|
return cached2;
|
|
@@ -27898,11 +28288,11 @@ var init_videoFrameInjector = __esm({
|
|
|
27898
28288
|
});
|
|
27899
28289
|
|
|
27900
28290
|
// ../engine/src/services/audioMixer.ts
|
|
27901
|
-
import { existsSync as
|
|
27902
|
-
import { isAbsolute as isAbsolute3, join as
|
|
28291
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync14, rmSync as rmSync6 } from "fs";
|
|
28292
|
+
import { isAbsolute as isAbsolute3, join as join24, dirname as dirname8 } from "path";
|
|
27903
28293
|
function parseAudioElements(html) {
|
|
27904
28294
|
const elements = [];
|
|
27905
|
-
const { document: document2 } = parseHTML(html);
|
|
28295
|
+
const { document: document2 } = parseHTML(unwrapTemplate(html));
|
|
27906
28296
|
const audioEls = document2.querySelectorAll("audio[id][src]");
|
|
27907
28297
|
for (const el of audioEls) {
|
|
27908
28298
|
const id = el.getAttribute("id");
|
|
@@ -27950,7 +28340,7 @@ function parseAudioElements(html) {
|
|
|
27950
28340
|
async function extractAudioFromVideo(videoPath, outputPath, options, signal, config) {
|
|
27951
28341
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
27952
28342
|
const outputDir = dirname8(outputPath);
|
|
27953
|
-
if (!
|
|
28343
|
+
if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
|
|
27954
28344
|
const args = ["-i", videoPath];
|
|
27955
28345
|
if (options?.startTime !== void 0) args.push("-ss", String(options.startTime));
|
|
27956
28346
|
if (options?.duration !== void 0) args.push("-t", String(options.duration));
|
|
@@ -27977,7 +28367,7 @@ async function extractAudioFromVideo(videoPath, outputPath, options, signal, con
|
|
|
27977
28367
|
async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, signal, config) {
|
|
27978
28368
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
27979
28369
|
const outputDir = dirname8(outputPath);
|
|
27980
|
-
if (!
|
|
28370
|
+
if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
|
|
27981
28371
|
const args = [
|
|
27982
28372
|
"-ss",
|
|
27983
28373
|
String(mediaStart),
|
|
@@ -28013,7 +28403,7 @@ async function prepareAudioTrack(srcPath, outputPath, mediaStart, duration, sign
|
|
|
28013
28403
|
async function generateSilence(outputPath, duration, signal, config) {
|
|
28014
28404
|
const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG2.ffmpegProcessTimeout;
|
|
28015
28405
|
const outputDir = dirname8(outputPath);
|
|
28016
|
-
if (!
|
|
28406
|
+
if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
|
|
28017
28407
|
const args = [
|
|
28018
28408
|
"-f",
|
|
28019
28409
|
"lavfi",
|
|
@@ -28056,7 +28446,7 @@ async function mixAudioTracks(tracks, outputPath, totalDuration, signal, config)
|
|
|
28056
28446
|
};
|
|
28057
28447
|
}
|
|
28058
28448
|
const outputDir = dirname8(outputPath);
|
|
28059
|
-
if (!
|
|
28449
|
+
if (!existsSync22(outputDir)) mkdirSync14(outputDir, { recursive: true });
|
|
28060
28450
|
const inputs = [];
|
|
28061
28451
|
const filterParts = [];
|
|
28062
28452
|
tracks.forEach((track, i2) => {
|
|
@@ -28117,7 +28507,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
28117
28507
|
const startMs = Date.now();
|
|
28118
28508
|
const tracks = [];
|
|
28119
28509
|
const errors = [];
|
|
28120
|
-
if (!
|
|
28510
|
+
if (!existsSync22(workDir)) mkdirSync14(workDir, { recursive: true });
|
|
28121
28511
|
await Promise.all(
|
|
28122
28512
|
elements.map(async (element) => {
|
|
28123
28513
|
if (signal?.aborted) {
|
|
@@ -28127,8 +28517,8 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
28127
28517
|
try {
|
|
28128
28518
|
let srcPath = element.src;
|
|
28129
28519
|
if (!isAbsolute3(srcPath) && !isHttpUrl(srcPath)) {
|
|
28130
|
-
const fromCompiled = compiledDir ?
|
|
28131
|
-
srcPath = fromCompiled &&
|
|
28520
|
+
const fromCompiled = compiledDir ? join24(compiledDir, srcPath) : null;
|
|
28521
|
+
srcPath = fromCompiled && existsSync22(fromCompiled) ? fromCompiled : join24(baseDir, srcPath);
|
|
28132
28522
|
}
|
|
28133
28523
|
if (isHttpUrl(srcPath)) {
|
|
28134
28524
|
try {
|
|
@@ -28140,7 +28530,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
28140
28530
|
return;
|
|
28141
28531
|
}
|
|
28142
28532
|
}
|
|
28143
|
-
if (!
|
|
28533
|
+
if (!existsSync22(srcPath)) {
|
|
28144
28534
|
errors.push(`Source not found: ${element.id} (${element.src})`);
|
|
28145
28535
|
return;
|
|
28146
28536
|
}
|
|
@@ -28151,7 +28541,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
28151
28541
|
}
|
|
28152
28542
|
let audioSrcPath = srcPath;
|
|
28153
28543
|
if (element.type === "video") {
|
|
28154
|
-
const extractedPath =
|
|
28544
|
+
const extractedPath = join24(workDir, `${element.id}-extracted.wav`);
|
|
28155
28545
|
const extractResult = await extractAudioFromVideo(
|
|
28156
28546
|
srcPath,
|
|
28157
28547
|
extractedPath,
|
|
@@ -28168,7 +28558,7 @@ async function processCompositionAudio(elements, baseDir, workDir, outputPath, t
|
|
|
28168
28558
|
}
|
|
28169
28559
|
audioSrcPath = extractedPath;
|
|
28170
28560
|
} else {
|
|
28171
|
-
const trimmedPath =
|
|
28561
|
+
const trimmedPath = join24(workDir, `${element.id}-trimmed.wav`);
|
|
28172
28562
|
const prepResult = await prepareAudioTrack(
|
|
28173
28563
|
srcPath,
|
|
28174
28564
|
trimmedPath,
|
|
@@ -28216,14 +28606,15 @@ var init_audioMixer = __esm({
|
|
|
28216
28606
|
init_urlDownloader();
|
|
28217
28607
|
init_config2();
|
|
28218
28608
|
init_runFfmpeg();
|
|
28609
|
+
init_htmlTemplate();
|
|
28219
28610
|
}
|
|
28220
28611
|
});
|
|
28221
28612
|
|
|
28222
28613
|
// ../engine/src/services/parallelCoordinator.ts
|
|
28223
28614
|
import { cpus as cpus2, freemem, totalmem as totalmem2 } from "os";
|
|
28224
|
-
import { existsSync as
|
|
28615
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync15, readdirSync as readdirSync10 } from "fs";
|
|
28225
28616
|
import { copyFile, rename } from "fs/promises";
|
|
28226
|
-
import { join as
|
|
28617
|
+
import { join as join25 } from "path";
|
|
28227
28618
|
function calculateOptimalWorkers(totalFrames, requested, config) {
|
|
28228
28619
|
const effectiveMaxWorkers = (() => {
|
|
28229
28620
|
const concurrency = config?.concurrency ?? DEFAULT_CONFIG2.concurrency;
|
|
@@ -28266,7 +28657,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
|
|
|
28266
28657
|
workerId: i2,
|
|
28267
28658
|
startFrame,
|
|
28268
28659
|
endFrame,
|
|
28269
|
-
outputDir:
|
|
28660
|
+
outputDir: join25(workDir, `worker-${i2}`)
|
|
28270
28661
|
});
|
|
28271
28662
|
}
|
|
28272
28663
|
return tasks;
|
|
@@ -28274,7 +28665,7 @@ function distributeFrames(totalFrames, workerCount, workDir) {
|
|
|
28274
28665
|
async function executeWorkerTask(task, serverUrl, captureOptions, createBeforeCaptureHook, signal, onFrameCaptured, onFrameBuffer, config) {
|
|
28275
28666
|
const startTime = Date.now();
|
|
28276
28667
|
let framesCaptured = 0;
|
|
28277
|
-
if (!
|
|
28668
|
+
if (!existsSync23(task.outputDir)) mkdirSync15(task.outputDir, { recursive: true });
|
|
28278
28669
|
let session = null;
|
|
28279
28670
|
let perf;
|
|
28280
28671
|
try {
|
|
@@ -28364,17 +28755,17 @@ async function executeParallelCapture(serverUrl, workDir, tasks, captureOptions,
|
|
|
28364
28755
|
return results;
|
|
28365
28756
|
}
|
|
28366
28757
|
async function mergeWorkerFrames(workDir, tasks, outputDir) {
|
|
28367
|
-
if (!
|
|
28758
|
+
if (!existsSync23(outputDir)) mkdirSync15(outputDir, { recursive: true });
|
|
28368
28759
|
let totalFrames = 0;
|
|
28369
28760
|
const sortedTasks = [...tasks].sort((a, b) => a.startFrame - b.startFrame);
|
|
28370
28761
|
for (const task of sortedTasks) {
|
|
28371
|
-
if (!
|
|
28762
|
+
if (!existsSync23(task.outputDir)) {
|
|
28372
28763
|
continue;
|
|
28373
28764
|
}
|
|
28374
|
-
const files =
|
|
28765
|
+
const files = readdirSync10(task.outputDir).filter((f3) => f3.startsWith("frame_") && (f3.endsWith(".jpg") || f3.endsWith(".png"))).sort();
|
|
28375
28766
|
const copyTasks = files.map(async (file) => {
|
|
28376
|
-
const sourcePath =
|
|
28377
|
-
const targetPath =
|
|
28767
|
+
const sourcePath = join25(task.outputDir, file);
|
|
28768
|
+
const targetPath = join25(outputDir, file);
|
|
28378
28769
|
try {
|
|
28379
28770
|
await rename(sourcePath, targetPath);
|
|
28380
28771
|
} catch {
|
|
@@ -28411,8 +28802,8 @@ var init_parallelCoordinator = __esm({
|
|
|
28411
28802
|
// ../engine/src/services/fileServer.ts
|
|
28412
28803
|
import { Hono as Hono2 } from "hono";
|
|
28413
28804
|
import { serve } from "@hono/node-server";
|
|
28414
|
-
import { readFileSync as readFileSync16, existsSync as
|
|
28415
|
-
import { join as
|
|
28805
|
+
import { readFileSync as readFileSync16, existsSync as existsSync24, statSync as statSync7 } from "fs";
|
|
28806
|
+
import { join as join26, extname as extname6 } from "path";
|
|
28416
28807
|
function stripEmbeddedRuntimeScripts(html) {
|
|
28417
28808
|
if (!html) return html;
|
|
28418
28809
|
const scriptRe = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
@@ -28481,12 +28872,12 @@ function createFileServer(options) {
|
|
|
28481
28872
|
let requestPath = c2.req.path;
|
|
28482
28873
|
if (requestPath === "/") requestPath = "/index.html";
|
|
28483
28874
|
const relativePath = requestPath.replace(/^\//, "");
|
|
28484
|
-
const compiledPath = compiledDir ?
|
|
28875
|
+
const compiledPath = compiledDir ? join26(compiledDir, relativePath) : null;
|
|
28485
28876
|
const hasCompiledFile = Boolean(
|
|
28486
|
-
compiledPath &&
|
|
28877
|
+
compiledPath && existsSync24(compiledPath) && statSync7(compiledPath).isFile()
|
|
28487
28878
|
);
|
|
28488
|
-
const filePath = hasCompiledFile ? compiledPath :
|
|
28489
|
-
if (!
|
|
28879
|
+
const filePath = hasCompiledFile ? compiledPath : join26(projectDir, relativePath);
|
|
28880
|
+
if (!existsSync24(filePath) || !statSync7(filePath).isFile()) {
|
|
28490
28881
|
return c2.text("Not found", 404);
|
|
28491
28882
|
}
|
|
28492
28883
|
const ext = extname6(filePath).toLowerCase();
|
|
@@ -29764,8 +30155,8 @@ var init_shaderTransitions = __esm({
|
|
|
29764
30155
|
});
|
|
29765
30156
|
|
|
29766
30157
|
// ../engine/src/services/hdrCapture.ts
|
|
29767
|
-
import { existsSync as
|
|
29768
|
-
import { join as
|
|
30158
|
+
import { existsSync as existsSync25, readdirSync as readdirSync11 } from "fs";
|
|
30159
|
+
import { join as join27 } from "path";
|
|
29769
30160
|
import { homedir as homedir6 } from "os";
|
|
29770
30161
|
function linearToPQ(L2) {
|
|
29771
30162
|
const Lp = Math.max(0, L2 * SDR_NITS / PQ_MAX_NITS);
|
|
@@ -29881,12 +30272,12 @@ function float16ToPqRgb(rawBuffer, bytesPerRow, width, height) {
|
|
|
29881
30272
|
return output;
|
|
29882
30273
|
}
|
|
29883
30274
|
function resolveHeadedChromePath() {
|
|
29884
|
-
const baseDir =
|
|
29885
|
-
if (!
|
|
29886
|
-
const versions =
|
|
30275
|
+
const baseDir = join27(homedir6(), ".cache", "puppeteer", "chrome");
|
|
30276
|
+
if (!existsSync25(baseDir)) return void 0;
|
|
30277
|
+
const versions = readdirSync11(baseDir).sort().reverse();
|
|
29887
30278
|
for (const version of versions) {
|
|
29888
30279
|
const candidates = [
|
|
29889
|
-
|
|
30280
|
+
join27(
|
|
29890
30281
|
baseDir,
|
|
29891
30282
|
version,
|
|
29892
30283
|
"chrome-mac-arm64",
|
|
@@ -29895,7 +30286,7 @@ function resolveHeadedChromePath() {
|
|
|
29895
30286
|
"MacOS",
|
|
29896
30287
|
"Google Chrome for Testing"
|
|
29897
30288
|
),
|
|
29898
|
-
|
|
30289
|
+
join27(
|
|
29899
30290
|
baseDir,
|
|
29900
30291
|
version,
|
|
29901
30292
|
"chrome-mac-x64",
|
|
@@ -29904,11 +30295,11 @@ function resolveHeadedChromePath() {
|
|
|
29904
30295
|
"MacOS",
|
|
29905
30296
|
"Google Chrome for Testing"
|
|
29906
30297
|
),
|
|
29907
|
-
|
|
29908
|
-
|
|
30298
|
+
join27(baseDir, version, "chrome-linux64", "chrome"),
|
|
30299
|
+
join27(baseDir, version, "chrome-win64", "chrome.exe")
|
|
29909
30300
|
];
|
|
29910
30301
|
for (const binary of candidates) {
|
|
29911
|
-
if (
|
|
30302
|
+
if (existsSync25(binary)) return binary;
|
|
29912
30303
|
}
|
|
29913
30304
|
}
|
|
29914
30305
|
return void 0;
|
|
@@ -30179,10 +30570,10 @@ var init_staticGuard = __esm({
|
|
|
30179
30570
|
});
|
|
30180
30571
|
|
|
30181
30572
|
// ../core/src/compiler/htmlBundler.ts
|
|
30182
|
-
import { readFileSync as readFileSync17, existsSync as
|
|
30183
|
-
import { join as
|
|
30573
|
+
import { readFileSync as readFileSync17, existsSync as existsSync26 } from "fs";
|
|
30574
|
+
import { join as join28, resolve as resolve12, isAbsolute as isAbsolute4, sep as sep2 } from "path";
|
|
30184
30575
|
import { transformSync } from "esbuild";
|
|
30185
|
-
function
|
|
30576
|
+
function parseHTMLContent2(html) {
|
|
30186
30577
|
const trimmed = html.trimStart().toLowerCase();
|
|
30187
30578
|
if (trimmed.startsWith("<!doctype") || trimmed.startsWith("<html")) {
|
|
30188
30579
|
return parseHTML(html).document;
|
|
@@ -30248,7 +30639,7 @@ function isRelativeUrl(url) {
|
|
|
30248
30639
|
return !url.startsWith("http://") && !url.startsWith("https://") && !url.startsWith("//") && !url.startsWith("data:") && !isAbsolute4(url);
|
|
30249
30640
|
}
|
|
30250
30641
|
function safeReadFile(filePath) {
|
|
30251
|
-
if (!
|
|
30642
|
+
if (!existsSync26(filePath)) return null;
|
|
30252
30643
|
try {
|
|
30253
30644
|
return readFileSync17(filePath, "utf-8");
|
|
30254
30645
|
} catch {
|
|
@@ -30256,7 +30647,7 @@ function safeReadFile(filePath) {
|
|
|
30256
30647
|
}
|
|
30257
30648
|
}
|
|
30258
30649
|
function safeReadFileBuffer(filePath) {
|
|
30259
|
-
if (!
|
|
30650
|
+
if (!existsSync26(filePath)) return null;
|
|
30260
30651
|
try {
|
|
30261
30652
|
return readFileSync17(filePath);
|
|
30262
30653
|
} catch {
|
|
@@ -30449,8 +30840,8 @@ function stripJsCommentsParserSafe(source) {
|
|
|
30449
30840
|
}
|
|
30450
30841
|
}
|
|
30451
30842
|
async function bundleToSingleHtml(projectDir, options) {
|
|
30452
|
-
const indexPath =
|
|
30453
|
-
if (!
|
|
30843
|
+
const indexPath = join28(projectDir, "index.html");
|
|
30844
|
+
if (!existsSync26(indexPath)) throw new Error("index.html not found in project directory");
|
|
30454
30845
|
const rawHtml = readFileSync17(indexPath, "utf-8");
|
|
30455
30846
|
const compiled = await compileHtml(rawHtml, projectDir, options?.probeMediaDuration);
|
|
30456
30847
|
const staticGuard = validateHyperframeHtmlContract(compiled);
|
|
@@ -30531,11 +30922,11 @@ async function bundleToSingleHtml(projectDir, options) {
|
|
|
30531
30922
|
console.warn(`[Bundler] Composition file not found: ${src}`);
|
|
30532
30923
|
continue;
|
|
30533
30924
|
}
|
|
30534
|
-
const compDoc =
|
|
30925
|
+
const compDoc = parseHTMLContent2(compHtml);
|
|
30535
30926
|
const compId = hostEl.getAttribute("data-composition-id");
|
|
30536
30927
|
const contentRoot = compDoc.querySelector("template");
|
|
30537
30928
|
const contentHtml = contentRoot ? contentRoot.innerHTML || "" : compDoc.body.innerHTML || "";
|
|
30538
|
-
const contentDoc =
|
|
30929
|
+
const contentDoc = parseHTMLContent2(contentHtml);
|
|
30539
30930
|
const innerRoot = compId ? contentDoc.querySelector(`[data-composition-id="${compId}"]`) : contentDoc.querySelector("[data-composition-id]");
|
|
30540
30931
|
if (!contentRoot && compDoc.head) {
|
|
30541
30932
|
for (const s2 of [...compDoc.head.querySelectorAll("style")]) {
|
|
@@ -30600,7 +30991,7 @@ async function bundleToSingleHtml(projectDir, options) {
|
|
|
30600
30991
|
if (!host) continue;
|
|
30601
30992
|
if (host.children.length > 0) continue;
|
|
30602
30993
|
const templateHtml = templateEl.innerHTML || "";
|
|
30603
|
-
const innerDoc =
|
|
30994
|
+
const innerDoc = parseHTMLContent2(templateHtml);
|
|
30604
30995
|
const innerRoot = innerDoc.querySelector(`[data-composition-id="${compId}"]`);
|
|
30605
30996
|
if (innerRoot) {
|
|
30606
30997
|
for (const styleEl of [...innerRoot.querySelectorAll("style")]) {
|
|
@@ -30725,8 +31116,8 @@ var init_compiler = __esm({
|
|
|
30725
31116
|
});
|
|
30726
31117
|
|
|
30727
31118
|
// ../producer/src/services/hyperframeRuntimeLoader.ts
|
|
30728
|
-
import { createHash as
|
|
30729
|
-
import { existsSync as
|
|
31119
|
+
import { createHash as createHash3 } from "crypto";
|
|
31120
|
+
import { existsSync as existsSync27, readFileSync as readFileSync18 } from "fs";
|
|
30730
31121
|
import { dirname as dirname9, resolve as resolve13 } from "path";
|
|
30731
31122
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
30732
31123
|
function resolveHyperframeManifestPath() {
|
|
@@ -30739,7 +31130,7 @@ function resolveHyperframeManifestPath() {
|
|
|
30739
31130
|
MODULE_RELATIVE_MANIFEST_PATH
|
|
30740
31131
|
];
|
|
30741
31132
|
for (const candidate of candidates) {
|
|
30742
|
-
if (
|
|
31133
|
+
if (existsSync27(candidate)) {
|
|
30743
31134
|
return candidate;
|
|
30744
31135
|
}
|
|
30745
31136
|
}
|
|
@@ -30750,7 +31141,7 @@ function getVerifiedHyperframeRuntimeSource() {
|
|
|
30750
31141
|
}
|
|
30751
31142
|
function resolveVerifiedHyperframeRuntime() {
|
|
30752
31143
|
const manifestPath = resolveHyperframeManifestPath();
|
|
30753
|
-
if (!
|
|
31144
|
+
if (!existsSync27(manifestPath)) {
|
|
30754
31145
|
throw new Error(
|
|
30755
31146
|
`[HyperframeRuntimeLoader] Missing manifest at ${manifestPath}. Build core runtime artifacts before rendering.`
|
|
30756
31147
|
);
|
|
@@ -30764,11 +31155,11 @@ function resolveVerifiedHyperframeRuntime() {
|
|
|
30764
31155
|
);
|
|
30765
31156
|
}
|
|
30766
31157
|
const runtimePath = resolve13(dirname9(manifestPath), runtimeFileName);
|
|
30767
|
-
if (!
|
|
31158
|
+
if (!existsSync27(runtimePath)) {
|
|
30768
31159
|
throw new Error(`[HyperframeRuntimeLoader] Missing runtime artifact at ${runtimePath}.`);
|
|
30769
31160
|
}
|
|
30770
31161
|
const runtimeSource = readFileSync18(runtimePath, "utf8");
|
|
30771
|
-
const runtimeSha =
|
|
31162
|
+
const runtimeSha = createHash3("sha256").update(runtimeSource, "utf8").digest("hex");
|
|
30772
31163
|
if (runtimeSha !== manifest.sha256) {
|
|
30773
31164
|
throw new Error(
|
|
30774
31165
|
`[HyperframeRuntimeLoader] Runtime checksum mismatch. expected=${manifest.sha256} actual=${runtimeSha}`
|
|
@@ -30806,16 +31197,16 @@ var init_hyperframeRuntimeLoader = __esm({
|
|
|
30806
31197
|
// ../producer/src/services/fileServer.ts
|
|
30807
31198
|
import { Hono as Hono3 } from "hono";
|
|
30808
31199
|
import { serve as serve2 } from "@hono/node-server";
|
|
30809
|
-
import { readFileSync as readFileSync19, existsSync as
|
|
30810
|
-
import { join as
|
|
31200
|
+
import { readFileSync as readFileSync19, existsSync as existsSync28, realpathSync, statSync as statSync8 } from "fs";
|
|
31201
|
+
import { join as join29, extname as extname7, resolve as resolve14, sep as sep3 } from "path";
|
|
30811
31202
|
function isPathInside(child, parent, options = {}) {
|
|
30812
31203
|
const { resolveSymlinks = false, pathModule } = options;
|
|
30813
31204
|
const resolveFn = pathModule?.resolve ?? resolve14;
|
|
30814
31205
|
const separator = pathModule?.sep ?? sep3;
|
|
30815
31206
|
const resolvedChild = resolveFn(child);
|
|
30816
31207
|
const resolvedParent = resolveFn(parent);
|
|
30817
|
-
const normalizedChild = resolveSymlinks &&
|
|
30818
|
-
const normalizedParent = resolveSymlinks &&
|
|
31208
|
+
const normalizedChild = resolveSymlinks && existsSync28(resolvedChild) ? realpathSync.native(resolvedChild) : resolvedChild;
|
|
31209
|
+
const normalizedParent = resolveSymlinks && existsSync28(resolvedParent) ? realpathSync.native(resolvedParent) : resolvedParent;
|
|
30819
31210
|
if (normalizedChild === normalizedParent) return true;
|
|
30820
31211
|
const parentWithSep = normalizedParent.endsWith(separator) ? normalizedParent : normalizedParent + separator;
|
|
30821
31212
|
return normalizedChild.startsWith(parentWithSep);
|
|
@@ -30904,14 +31295,14 @@ function createFileServer2(options) {
|
|
|
30904
31295
|
const relativePath = requestPath.replace(/^\//, "");
|
|
30905
31296
|
let filePath = null;
|
|
30906
31297
|
if (compiledDir) {
|
|
30907
|
-
const candidate =
|
|
30908
|
-
if (
|
|
31298
|
+
const candidate = join29(compiledDir, relativePath);
|
|
31299
|
+
if (existsSync28(candidate) && isPathInside(candidate, compiledDir, { resolveSymlinks: true }) && statSync8(candidate).isFile()) {
|
|
30909
31300
|
filePath = candidate;
|
|
30910
31301
|
}
|
|
30911
31302
|
}
|
|
30912
31303
|
if (!filePath) {
|
|
30913
|
-
const candidate =
|
|
30914
|
-
if (
|
|
31304
|
+
const candidate = join29(projectDir, relativePath);
|
|
31305
|
+
if (existsSync28(candidate) && isPathInside(candidate, projectDir, { resolveSymlinks: true }) && statSync8(candidate).isFile()) {
|
|
30915
31306
|
filePath = candidate;
|
|
30916
31307
|
}
|
|
30917
31308
|
}
|
|
@@ -31285,7 +31676,7 @@ var init_ffprobe2 = __esm({
|
|
|
31285
31676
|
});
|
|
31286
31677
|
|
|
31287
31678
|
// ../producer/src/utils/paths.ts
|
|
31288
|
-
import { resolve as resolve15, basename as basename2, join as
|
|
31679
|
+
import { resolve as resolve15, basename as basename2, join as join30, relative as relative2, isAbsolute as isAbsolute5 } from "path";
|
|
31289
31680
|
function isPathInside2(childPath, parentPath) {
|
|
31290
31681
|
const absChild = resolve15(childPath);
|
|
31291
31682
|
const absParent = resolve15(parentPath);
|
|
@@ -31306,7 +31697,7 @@ function toExternalAssetKey(absPath) {
|
|
|
31306
31697
|
function resolveRenderPaths(projectDir, outputPath, rendersDir = DEFAULT_RENDERS_DIR) {
|
|
31307
31698
|
const absoluteProjectDir = resolve15(projectDir);
|
|
31308
31699
|
const projectName = basename2(absoluteProjectDir);
|
|
31309
|
-
const resolvedOutputPath = outputPath ??
|
|
31700
|
+
const resolvedOutputPath = outputPath ?? join30(rendersDir, `${projectName}.mp4`);
|
|
31310
31701
|
const absoluteOutputPath = resolve15(resolvedOutputPath);
|
|
31311
31702
|
return { absoluteProjectDir, absoluteOutputPath };
|
|
31312
31703
|
}
|
|
@@ -31379,9 +31770,9 @@ var init_fontData_generated = __esm({
|
|
|
31379
31770
|
});
|
|
31380
31771
|
|
|
31381
31772
|
// ../producer/src/services/deterministicFonts.ts
|
|
31382
|
-
import { existsSync as
|
|
31773
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as readFileSync20, writeFileSync as writeFileSync11 } from "fs";
|
|
31383
31774
|
import { homedir as homedir7 } from "os";
|
|
31384
|
-
import { join as
|
|
31775
|
+
import { join as join31 } from "path";
|
|
31385
31776
|
function normalizeFamilyName(family) {
|
|
31386
31777
|
return family.trim().replace(/^['"]|['"]$/g, "").trim().toLowerCase();
|
|
31387
31778
|
}
|
|
@@ -31492,14 +31883,14 @@ function fontSlug(familyName) {
|
|
|
31492
31883
|
return familyName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
31493
31884
|
}
|
|
31494
31885
|
function fontCacheDir(slug) {
|
|
31495
|
-
const dir =
|
|
31496
|
-
if (!
|
|
31497
|
-
|
|
31886
|
+
const dir = join31(GOOGLE_FONTS_CACHE_DIR, slug);
|
|
31887
|
+
if (!existsSync29(dir)) {
|
|
31888
|
+
mkdirSync16(dir, { recursive: true });
|
|
31498
31889
|
}
|
|
31499
31890
|
return dir;
|
|
31500
31891
|
}
|
|
31501
31892
|
function cachedWoff2Path(slug, weight, style) {
|
|
31502
|
-
return
|
|
31893
|
+
return join31(fontCacheDir(slug), `${weight}-${style}.woff2`);
|
|
31503
31894
|
}
|
|
31504
31895
|
async function fetchGoogleFont(familyName) {
|
|
31505
31896
|
const slug = fontSlug(familyName);
|
|
@@ -31525,12 +31916,12 @@ async function fetchGoogleFont(familyName) {
|
|
|
31525
31916
|
const woff2Url = match[3] || "";
|
|
31526
31917
|
if (!woff2Url) continue;
|
|
31527
31918
|
const cachePath2 = cachedWoff2Path(slug, weight, style);
|
|
31528
|
-
if (!
|
|
31919
|
+
if (!existsSync29(cachePath2)) {
|
|
31529
31920
|
try {
|
|
31530
31921
|
const fontRes = await fetch(woff2Url);
|
|
31531
31922
|
if (!fontRes.ok) continue;
|
|
31532
31923
|
const buffer = Buffer.from(await fontRes.arrayBuffer());
|
|
31533
|
-
|
|
31924
|
+
writeFileSync11(cachePath2, buffer);
|
|
31534
31925
|
} catch {
|
|
31535
31926
|
continue;
|
|
31536
31927
|
}
|
|
@@ -31711,14 +32102,14 @@ var init_deterministicFonts = __esm({
|
|
|
31711
32102
|
poppins: "poppins",
|
|
31712
32103
|
"segoe ui": "roboto"
|
|
31713
32104
|
};
|
|
31714
|
-
GOOGLE_FONTS_CACHE_DIR =
|
|
32105
|
+
GOOGLE_FONTS_CACHE_DIR = join31(homedir7(), ".cache", "hyperframes", "fonts");
|
|
31715
32106
|
WOFF2_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";
|
|
31716
32107
|
}
|
|
31717
32108
|
});
|
|
31718
32109
|
|
|
31719
32110
|
// ../producer/src/services/htmlCompiler.ts
|
|
31720
|
-
import { readFileSync as readFileSync21, existsSync as
|
|
31721
|
-
import { join as
|
|
32111
|
+
import { readFileSync as readFileSync21, existsSync as existsSync30, mkdirSync as mkdirSync17 } from "fs";
|
|
32112
|
+
import { join as join32, dirname as dirname10, resolve as resolve16 } from "path";
|
|
31722
32113
|
import postcss from "postcss";
|
|
31723
32114
|
function dedupeElementsById(elements) {
|
|
31724
32115
|
const deduped = /* @__PURE__ */ new Map();
|
|
@@ -31769,16 +32160,16 @@ function detectRenderModeHints(html) {
|
|
|
31769
32160
|
async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagName19) {
|
|
31770
32161
|
let filePath = src;
|
|
31771
32162
|
if (isHttpUrl(src)) {
|
|
31772
|
-
if (!
|
|
32163
|
+
if (!existsSync30(downloadDir)) mkdirSync17(downloadDir, { recursive: true });
|
|
31773
32164
|
try {
|
|
31774
32165
|
filePath = await downloadToTemp(src, downloadDir);
|
|
31775
32166
|
} catch {
|
|
31776
32167
|
return { duration: 0, resolvedPath: src };
|
|
31777
32168
|
}
|
|
31778
32169
|
} else if (!filePath.startsWith("/")) {
|
|
31779
|
-
filePath =
|
|
32170
|
+
filePath = join32(baseDir, filePath);
|
|
31780
32171
|
}
|
|
31781
|
-
if (!
|
|
32172
|
+
if (!existsSync30(filePath)) {
|
|
31782
32173
|
return { duration: 0, resolvedPath: filePath };
|
|
31783
32174
|
}
|
|
31784
32175
|
const metadata = tagName19 === "video" ? await extractMediaMetadata(filePath) : await extractAudioMetadata(filePath);
|
|
@@ -31847,7 +32238,7 @@ async function parseSubCompositions(html, projectDir, downloadDir, parentOffset
|
|
|
31847
32238
|
if (visited.has(filePath)) {
|
|
31848
32239
|
continue;
|
|
31849
32240
|
}
|
|
31850
|
-
if (!
|
|
32241
|
+
if (!existsSync30(filePath)) {
|
|
31851
32242
|
continue;
|
|
31852
32243
|
}
|
|
31853
32244
|
const rawSubHtml = readFileSync21(filePath, "utf-8");
|
|
@@ -32057,7 +32448,7 @@ function inlineSubCompositions(html, subCompositions, projectDir) {
|
|
|
32057
32448
|
let compHtml = subCompositions.get(srcPath) || null;
|
|
32058
32449
|
if (!compHtml) {
|
|
32059
32450
|
const filePath = resolve16(projectDir, srcPath);
|
|
32060
|
-
if (
|
|
32451
|
+
if (existsSync30(filePath)) {
|
|
32061
32452
|
compHtml = readFileSync21(filePath, "utf-8");
|
|
32062
32453
|
}
|
|
32063
32454
|
}
|
|
@@ -32285,7 +32676,7 @@ function collectExternalAssets(html, projectDir) {
|
|
|
32285
32676
|
if (isPathInside2(absPath, absProjectDir)) {
|
|
32286
32677
|
return null;
|
|
32287
32678
|
}
|
|
32288
|
-
if (!
|
|
32679
|
+
if (!existsSync30(absPath)) return null;
|
|
32289
32680
|
const safeKey = toExternalAssetKey(absPath);
|
|
32290
32681
|
externalAssets.set(safeKey, absPath);
|
|
32291
32682
|
return safeKey;
|
|
@@ -32481,9 +32872,10 @@ async function recompileWithResolutions(compiled, resolutions, projectDir, downl
|
|
|
32481
32872
|
const mainVideos = parseVideoElements(html);
|
|
32482
32873
|
const mainAudios = parseAudioElements(html);
|
|
32483
32874
|
const mainImages = parseImageElements(html);
|
|
32484
|
-
const
|
|
32485
|
-
const
|
|
32486
|
-
const
|
|
32875
|
+
const hasSubMedia = subVideos.length > 0 || subAudios.length > 0 || subImages.length > 0;
|
|
32876
|
+
const videos = hasSubMedia ? dedupeElementsById([...mainVideos, ...subVideos]) : compiled.videos;
|
|
32877
|
+
const audios = hasSubMedia ? dedupeElementsById([...mainAudios, ...subAudios]) : compiled.audios;
|
|
32878
|
+
const images = hasSubMedia ? dedupeElementsById([...mainImages, ...subImages]) : compiled.images;
|
|
32487
32879
|
const remaining = compiled.unresolvedCompositions.filter(
|
|
32488
32880
|
(c2) => !resolutions.some((r2) => r2.id === c2.id)
|
|
32489
32881
|
);
|
|
@@ -32560,18 +32952,124 @@ var init_logger = __esm({
|
|
|
32560
32952
|
}
|
|
32561
32953
|
});
|
|
32562
32954
|
|
|
32955
|
+
// ../producer/src/services/frameDirCache.ts
|
|
32956
|
+
import { readdirSync as readdirSync12 } from "fs";
|
|
32957
|
+
function getMaxFrameIndex(frameDir) {
|
|
32958
|
+
const cached2 = cache.get(frameDir);
|
|
32959
|
+
if (cached2 !== void 0) {
|
|
32960
|
+
cache.delete(frameDir);
|
|
32961
|
+
cache.set(frameDir, cached2);
|
|
32962
|
+
return cached2;
|
|
32963
|
+
}
|
|
32964
|
+
let max = 0;
|
|
32965
|
+
try {
|
|
32966
|
+
for (const name of readdirSync12(frameDir)) {
|
|
32967
|
+
const m2 = FRAME_FILENAME_RE.exec(name);
|
|
32968
|
+
if (!m2) continue;
|
|
32969
|
+
const n = Number(m2[1]);
|
|
32970
|
+
if (Number.isFinite(n) && n > max) max = n;
|
|
32971
|
+
}
|
|
32972
|
+
} catch {
|
|
32973
|
+
}
|
|
32974
|
+
if (cache.size >= MAX_ENTRIES) {
|
|
32975
|
+
const oldest = cache.keys().next().value;
|
|
32976
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
32977
|
+
}
|
|
32978
|
+
cache.set(frameDir, max);
|
|
32979
|
+
return max;
|
|
32980
|
+
}
|
|
32981
|
+
function clearMaxFrameIndex(frameDir) {
|
|
32982
|
+
return cache.delete(frameDir);
|
|
32983
|
+
}
|
|
32984
|
+
var cache, FRAME_FILENAME_RE, MAX_ENTRIES;
|
|
32985
|
+
var init_frameDirCache = __esm({
|
|
32986
|
+
"../producer/src/services/frameDirCache.ts"() {
|
|
32987
|
+
"use strict";
|
|
32988
|
+
cache = /* @__PURE__ */ new Map();
|
|
32989
|
+
FRAME_FILENAME_RE = /^frame_(\d+)\.png$/;
|
|
32990
|
+
MAX_ENTRIES = 1e3;
|
|
32991
|
+
}
|
|
32992
|
+
});
|
|
32993
|
+
|
|
32994
|
+
// ../producer/src/services/hdrImageTransferCache.ts
|
|
32995
|
+
function createHdrImageTransferCache(options = {}) {
|
|
32996
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
32997
|
+
if (!Number.isInteger(maxBytes) || maxBytes < 0) {
|
|
32998
|
+
throw new Error(
|
|
32999
|
+
`createHdrImageTransferCache: maxBytes must be a non-negative integer, got ${String(maxBytes)}`
|
|
33000
|
+
);
|
|
33001
|
+
}
|
|
33002
|
+
const entries2 = /* @__PURE__ */ new Map();
|
|
33003
|
+
let totalBytes = 0;
|
|
33004
|
+
function makeKey(imageId, targetTransfer) {
|
|
33005
|
+
return `${imageId}|${targetTransfer}`;
|
|
33006
|
+
}
|
|
33007
|
+
function evictUntilRoom(needed) {
|
|
33008
|
+
while (totalBytes + needed > maxBytes && entries2.size > 0) {
|
|
33009
|
+
const lruKey = entries2.keys().next().value;
|
|
33010
|
+
if (lruKey === void 0) break;
|
|
33011
|
+
const evicted = entries2.get(lruKey);
|
|
33012
|
+
if (evicted) totalBytes -= evicted.byteLength;
|
|
33013
|
+
entries2.delete(lruKey);
|
|
33014
|
+
}
|
|
33015
|
+
}
|
|
33016
|
+
return {
|
|
33017
|
+
getConverted(imageId, sourceTransfer, targetTransfer, source) {
|
|
33018
|
+
if (sourceTransfer === targetTransfer) {
|
|
33019
|
+
return source;
|
|
33020
|
+
}
|
|
33021
|
+
if (maxBytes === 0) {
|
|
33022
|
+
const fresh = Buffer.from(source);
|
|
33023
|
+
convertTransfer(fresh, sourceTransfer, targetTransfer);
|
|
33024
|
+
return fresh;
|
|
33025
|
+
}
|
|
33026
|
+
const key2 = makeKey(imageId, targetTransfer);
|
|
33027
|
+
const existing = entries2.get(key2);
|
|
33028
|
+
if (existing) {
|
|
33029
|
+
entries2.delete(key2);
|
|
33030
|
+
entries2.set(key2, existing);
|
|
33031
|
+
return existing;
|
|
33032
|
+
}
|
|
33033
|
+
const converted = Buffer.from(source);
|
|
33034
|
+
convertTransfer(converted, sourceTransfer, targetTransfer);
|
|
33035
|
+
if (converted.byteLength > maxBytes) {
|
|
33036
|
+
return converted;
|
|
33037
|
+
}
|
|
33038
|
+
evictUntilRoom(converted.byteLength);
|
|
33039
|
+
entries2.set(key2, converted);
|
|
33040
|
+
totalBytes += converted.byteLength;
|
|
33041
|
+
return converted;
|
|
33042
|
+
},
|
|
33043
|
+
size() {
|
|
33044
|
+
return entries2.size;
|
|
33045
|
+
},
|
|
33046
|
+
bytesUsed() {
|
|
33047
|
+
return totalBytes;
|
|
33048
|
+
}
|
|
33049
|
+
};
|
|
33050
|
+
}
|
|
33051
|
+
var DEFAULT_MAX_BYTES;
|
|
33052
|
+
var init_hdrImageTransferCache = __esm({
|
|
33053
|
+
"../producer/src/services/hdrImageTransferCache.ts"() {
|
|
33054
|
+
"use strict";
|
|
33055
|
+
init_src2();
|
|
33056
|
+
DEFAULT_MAX_BYTES = 200 * 1024 * 1024;
|
|
33057
|
+
}
|
|
33058
|
+
});
|
|
33059
|
+
|
|
32563
33060
|
// ../producer/src/services/renderOrchestrator.ts
|
|
32564
33061
|
import {
|
|
32565
|
-
existsSync as
|
|
32566
|
-
mkdirSync as
|
|
33062
|
+
existsSync as existsSync31,
|
|
33063
|
+
mkdirSync as mkdirSync18,
|
|
32567
33064
|
rmSync as rmSync7,
|
|
32568
33065
|
readFileSync as readFileSync22,
|
|
32569
|
-
readdirSync as
|
|
32570
|
-
|
|
33066
|
+
readdirSync as readdirSync13,
|
|
33067
|
+
statSync as statSync9,
|
|
33068
|
+
writeFileSync as writeFileSync12,
|
|
32571
33069
|
copyFileSync as copyFileSync2,
|
|
32572
33070
|
appendFileSync
|
|
32573
33071
|
} from "fs";
|
|
32574
|
-
import { join as
|
|
33072
|
+
import { join as join33, dirname as dirname11, resolve as resolve17 } from "path";
|
|
32575
33073
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
32576
33074
|
import { freemem as freemem2 } from "os";
|
|
32577
33075
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -32584,21 +33082,32 @@ async function safeCleanup(label2, fn, log2 = defaultLogger) {
|
|
|
32584
33082
|
});
|
|
32585
33083
|
}
|
|
32586
33084
|
}
|
|
32587
|
-
function
|
|
32588
|
-
|
|
32589
|
-
|
|
32590
|
-
|
|
32591
|
-
|
|
32592
|
-
|
|
32593
|
-
|
|
32594
|
-
|
|
32595
|
-
|
|
32596
|
-
|
|
33085
|
+
function sampleDirectoryBytes(dir) {
|
|
33086
|
+
let total = 0;
|
|
33087
|
+
const stack = [dir];
|
|
33088
|
+
while (stack.length > 0) {
|
|
33089
|
+
const current = stack.pop();
|
|
33090
|
+
if (!current) continue;
|
|
33091
|
+
let entries2 = [];
|
|
33092
|
+
try {
|
|
33093
|
+
entries2 = readdirSync13(current);
|
|
33094
|
+
} catch {
|
|
33095
|
+
continue;
|
|
33096
|
+
}
|
|
33097
|
+
for (const name of entries2) {
|
|
33098
|
+
const full = join33(current, name);
|
|
33099
|
+
try {
|
|
33100
|
+
const st2 = statSync9(full);
|
|
33101
|
+
if (st2.isDirectory()) {
|
|
33102
|
+
stack.push(full);
|
|
33103
|
+
} else if (st2.isFile()) {
|
|
33104
|
+
total += st2.size;
|
|
33105
|
+
}
|
|
33106
|
+
} catch {
|
|
33107
|
+
}
|
|
32597
33108
|
}
|
|
32598
|
-
} catch {
|
|
32599
33109
|
}
|
|
32600
|
-
|
|
32601
|
-
return max;
|
|
33110
|
+
return total;
|
|
32602
33111
|
}
|
|
32603
33112
|
function countNonZeroAlpha(rgba) {
|
|
32604
33113
|
let n = 0;
|
|
@@ -32615,6 +33124,9 @@ function countNonZeroRgb48(buf) {
|
|
|
32615
33124
|
}
|
|
32616
33125
|
return n;
|
|
32617
33126
|
}
|
|
33127
|
+
function projectBrowserEndToCompositionTimeline(existingStart, browserStart, browserEnd) {
|
|
33128
|
+
return browserEnd + (existingStart - browserStart);
|
|
33129
|
+
}
|
|
32618
33130
|
function updateJobStatus(job, status, stage, progress, onProgress) {
|
|
32619
33131
|
job.status = status;
|
|
32620
33132
|
job.currentStage = stage;
|
|
@@ -32658,21 +33170,21 @@ function installDebugLogger(logPath, log2 = defaultLogger) {
|
|
|
32658
33170
|
};
|
|
32659
33171
|
}
|
|
32660
33172
|
function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
32661
|
-
const compileDir =
|
|
32662
|
-
|
|
32663
|
-
|
|
33173
|
+
const compileDir = join33(workDir, "compiled");
|
|
33174
|
+
mkdirSync18(compileDir, { recursive: true });
|
|
33175
|
+
writeFileSync12(join33(compileDir, "index.html"), compiled.html, "utf-8");
|
|
32664
33176
|
for (const [srcPath, html] of compiled.subCompositions) {
|
|
32665
|
-
const outPath =
|
|
32666
|
-
|
|
32667
|
-
|
|
33177
|
+
const outPath = join33(compileDir, srcPath);
|
|
33178
|
+
mkdirSync18(dirname11(outPath), { recursive: true });
|
|
33179
|
+
writeFileSync12(outPath, html, "utf-8");
|
|
32668
33180
|
}
|
|
32669
33181
|
for (const [relativePath, absolutePath] of compiled.externalAssets) {
|
|
32670
|
-
const outPath = resolve17(
|
|
33182
|
+
const outPath = resolve17(join33(compileDir, relativePath));
|
|
32671
33183
|
if (!isPathInside2(outPath, compileDir)) {
|
|
32672
33184
|
console.warn(`[Render] Skipping external asset with unsafe path: ${relativePath}`);
|
|
32673
33185
|
continue;
|
|
32674
33186
|
}
|
|
32675
|
-
|
|
33187
|
+
mkdirSync18(dirname11(outPath), { recursive: true });
|
|
32676
33188
|
copyFileSync2(absolutePath, outPath);
|
|
32677
33189
|
}
|
|
32678
33190
|
if (includeSummary) {
|
|
@@ -32697,7 +33209,7 @@ function writeCompiledArtifacts(compiled, workDir, includeSummary) {
|
|
|
32697
33209
|
subCompositions: Array.from(compiled.subCompositions.keys()),
|
|
32698
33210
|
renderModeHints: compiled.renderModeHints
|
|
32699
33211
|
};
|
|
32700
|
-
|
|
33212
|
+
writeFileSync12(join33(compileDir, "summary.json"), JSON.stringify(summary, null, 2), "utf-8");
|
|
32701
33213
|
}
|
|
32702
33214
|
}
|
|
32703
33215
|
function applyRenderModeHints(cfg, compiled, log2 = defaultLogger) {
|
|
@@ -32718,8 +33230,8 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, w
|
|
|
32718
33230
|
if (videoFrameIndex < 1) return;
|
|
32719
33231
|
const maxIndex = getMaxFrameIndex(frameDir);
|
|
32720
33232
|
const effectiveIndex = maxIndex > 0 ? Math.min(videoFrameIndex, maxIndex) : videoFrameIndex;
|
|
32721
|
-
const framePath =
|
|
32722
|
-
if (!
|
|
33233
|
+
const framePath = join33(frameDir, `frame_${String(effectiveIndex).padStart(4, "0")}.png`);
|
|
33234
|
+
if (!existsSync31(framePath)) {
|
|
32723
33235
|
return;
|
|
32724
33236
|
}
|
|
32725
33237
|
try {
|
|
@@ -32765,17 +33277,13 @@ function blitHdrVideoLayer(canvas, el, time, fps, hdrFrameDirs, hdrStartTimes, w
|
|
|
32765
33277
|
}
|
|
32766
33278
|
}
|
|
32767
33279
|
}
|
|
32768
|
-
function blitHdrImageLayer(canvas, el, hdrImageBuffers, width, height, log2, sourceTransfer, targetTransfer) {
|
|
33280
|
+
function blitHdrImageLayer(canvas, el, hdrImageBuffers, hdrImageTransferCache, width, height, log2, sourceTransfer, targetTransfer) {
|
|
32769
33281
|
const buf = hdrImageBuffers.get(el.id);
|
|
32770
33282
|
if (!buf) {
|
|
32771
33283
|
return;
|
|
32772
33284
|
}
|
|
32773
33285
|
try {
|
|
32774
|
-
|
|
32775
|
-
if (sourceTransfer && targetTransfer && sourceTransfer !== targetTransfer) {
|
|
32776
|
-
hdrRgb = Buffer.from(buf.data);
|
|
32777
|
-
convertTransfer(hdrRgb, sourceTransfer, targetTransfer);
|
|
32778
|
-
}
|
|
33286
|
+
const hdrRgb = sourceTransfer && targetTransfer ? hdrImageTransferCache.getConverted(el.id, sourceTransfer, targetTransfer, buf.data) : buf.data;
|
|
32779
33287
|
const viewportMatrix = parseTransformMatrix(el.transform);
|
|
32780
33288
|
const br = el.borderRadius;
|
|
32781
33289
|
const hasBorderRadius = br[0] > 0 || br[1] > 0 || br[2] > 0 || br[3] > 0;
|
|
@@ -32825,6 +33333,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
32825
33333
|
effectiveHdr,
|
|
32826
33334
|
nativeHdrImageIds,
|
|
32827
33335
|
hdrImageBuffers,
|
|
33336
|
+
hdrImageTransferCache,
|
|
32828
33337
|
hdrFrameDirs,
|
|
32829
33338
|
hdrVideoStartTimes,
|
|
32830
33339
|
imageTransfers,
|
|
@@ -32864,6 +33373,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
32864
33373
|
canvas,
|
|
32865
33374
|
layer.element,
|
|
32866
33375
|
hdrImageBuffers,
|
|
33376
|
+
hdrImageTransferCache,
|
|
32867
33377
|
width,
|
|
32868
33378
|
height,
|
|
32869
33379
|
log2,
|
|
@@ -32904,7 +33414,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
32904
33414
|
const startTime = hdrVideoStartTimes.get(layer.element.id) ?? 0;
|
|
32905
33415
|
const localTime = time - startTime;
|
|
32906
33416
|
const frameNum = Math.floor(localTime * fps) + 1;
|
|
32907
|
-
const expectedFrame = frameDir ?
|
|
33417
|
+
const expectedFrame = frameDir ? join33(frameDir, `frame_${String(frameNum).padStart(4, "0")}.png`) : null;
|
|
32908
33418
|
log2.info("[diag] hdr layer blit", {
|
|
32909
33419
|
frame: debugFrameIndex,
|
|
32910
33420
|
layerIdx,
|
|
@@ -32916,7 +33426,7 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
32916
33426
|
localTime: localTime.toFixed(3),
|
|
32917
33427
|
hdrFrameNum: frameNum,
|
|
32918
33428
|
expectedFrame,
|
|
32919
|
-
expectedFrameExists: expectedFrame ?
|
|
33429
|
+
expectedFrameExists: expectedFrame ? existsSync31(expectedFrame) : false
|
|
32920
33430
|
});
|
|
32921
33431
|
}
|
|
32922
33432
|
}
|
|
@@ -32941,8 +33451,8 @@ async function compositeHdrFrame(ctx, canvas, time, fullStacking, elementFilter,
|
|
|
32941
33451
|
if (shouldLog && debugDumpDir) {
|
|
32942
33452
|
const after2 = countNonZeroRgb48(canvas);
|
|
32943
33453
|
const dumpName = `frame_${String(debugFrameIndex).padStart(4, "0")}_layer_${String(layerIdx).padStart(2, "0")}_dom.png`;
|
|
32944
|
-
const dumpPath =
|
|
32945
|
-
|
|
33454
|
+
const dumpPath = join33(debugDumpDir, dumpName);
|
|
33455
|
+
writeFileSync12(dumpPath, domPng);
|
|
32946
33456
|
log2.info("[diag] dom layer blit", {
|
|
32947
33457
|
frame: debugFrameIndex,
|
|
32948
33458
|
layerIdx,
|
|
@@ -33015,8 +33525,8 @@ function extractStandaloneEntryFromIndex(indexHtml, entryFile) {
|
|
|
33015
33525
|
async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSignal) {
|
|
33016
33526
|
const moduleDir = dirname11(fileURLToPath3(import.meta.url));
|
|
33017
33527
|
const producerRoot = process.env.PRODUCER_RENDERS_DIR ? resolve17(process.env.PRODUCER_RENDERS_DIR, "..") : resolve17(moduleDir, "../..");
|
|
33018
|
-
const debugDir =
|
|
33019
|
-
const workDir = job.config.debug ?
|
|
33528
|
+
const debugDir = join33(producerRoot, ".debug");
|
|
33529
|
+
const workDir = job.config.debug ? join33(debugDir, job.id) : join33(dirname11(outputPath), `work-${job.id}`);
|
|
33020
33530
|
const pipelineStart = Date.now();
|
|
33021
33531
|
const log2 = job.config.logger ?? defaultLogger;
|
|
33022
33532
|
let fileServer = null;
|
|
@@ -33028,7 +33538,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33028
33538
|
videoExtractionFailures: 0,
|
|
33029
33539
|
imageDecodeFailures: 0
|
|
33030
33540
|
};
|
|
33031
|
-
const perfOutputPath =
|
|
33541
|
+
const perfOutputPath = join33(workDir, "perf-summary.json");
|
|
33032
33542
|
const cfg = { ...job.config.producerConfig ?? resolveConfig() };
|
|
33033
33543
|
const outputFormat = job.config.format ?? "mp4";
|
|
33034
33544
|
const isWebm = outputFormat === "webm";
|
|
@@ -33061,22 +33571,22 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33061
33571
|
};
|
|
33062
33572
|
job.startedAt = /* @__PURE__ */ new Date();
|
|
33063
33573
|
assertNotAborted();
|
|
33064
|
-
if (!
|
|
33574
|
+
if (!existsSync31(workDir)) mkdirSync18(workDir, { recursive: true });
|
|
33065
33575
|
if (job.config.debug) {
|
|
33066
|
-
const logPath =
|
|
33576
|
+
const logPath = join33(workDir, "render.log");
|
|
33067
33577
|
restoreLogger = installDebugLogger(logPath, log2);
|
|
33068
33578
|
}
|
|
33069
33579
|
const entryFile = job.config.entryFile || "index.html";
|
|
33070
|
-
let htmlPath =
|
|
33071
|
-
if (!
|
|
33580
|
+
let htmlPath = join33(projectDir, entryFile);
|
|
33581
|
+
if (!existsSync31(htmlPath)) {
|
|
33072
33582
|
throw new Error(`Entry file not found: ${htmlPath}`);
|
|
33073
33583
|
}
|
|
33074
33584
|
assertNotAborted();
|
|
33075
33585
|
const rawEntry = readFileSync22(htmlPath, "utf-8");
|
|
33076
33586
|
if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
|
|
33077
|
-
const wrapperPath =
|
|
33078
|
-
const projectIndexPath =
|
|
33079
|
-
if (!
|
|
33587
|
+
const wrapperPath = join33(workDir, "standalone-entry.html");
|
|
33588
|
+
const projectIndexPath = join33(projectDir, "index.html");
|
|
33589
|
+
if (!existsSync31(projectIndexPath)) {
|
|
33080
33590
|
throw new Error(
|
|
33081
33591
|
`Template entry file "${entryFile}" requires a project index.html to extract its render shell.`
|
|
33082
33592
|
);
|
|
@@ -33090,7 +33600,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33090
33600
|
`Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
|
|
33091
33601
|
);
|
|
33092
33602
|
}
|
|
33093
|
-
|
|
33603
|
+
writeFileSync12(wrapperPath, standaloneHtml, "utf-8");
|
|
33094
33604
|
htmlPath = wrapperPath;
|
|
33095
33605
|
log2.info("Extracted standalone entry from index.html host context", {
|
|
33096
33606
|
entryFile
|
|
@@ -33099,7 +33609,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33099
33609
|
const stage1Start = Date.now();
|
|
33100
33610
|
updateJobStatus(job, "preprocessing", "Compiling composition", 5, onProgress);
|
|
33101
33611
|
const compileStart = Date.now();
|
|
33102
|
-
let compiled = await compileForRender(projectDir, htmlPath,
|
|
33612
|
+
let compiled = await compileForRender(projectDir, htmlPath, join33(workDir, "downloads"));
|
|
33103
33613
|
assertNotAborted();
|
|
33104
33614
|
perfStages.compileOnlyMs = Date.now() - compileStart;
|
|
33105
33615
|
applyRenderModeHints(cfg, compiled, log2);
|
|
@@ -33131,7 +33641,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33131
33641
|
reasons.push(`${compiled.unresolvedCompositions.length} unresolved composition(s)`);
|
|
33132
33642
|
fileServer = await createFileServer2({
|
|
33133
33643
|
projectDir,
|
|
33134
|
-
compiledDir:
|
|
33644
|
+
compiledDir: join33(workDir, "compiled"),
|
|
33135
33645
|
port: 0,
|
|
33136
33646
|
preHeadScripts: [VIRTUAL_TIME_SHIM]
|
|
33137
33647
|
});
|
|
@@ -33145,7 +33655,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33145
33655
|
};
|
|
33146
33656
|
probeSession = await createCaptureSession(
|
|
33147
33657
|
fileServer.url,
|
|
33148
|
-
|
|
33658
|
+
join33(workDir, "probe"),
|
|
33149
33659
|
captureOpts,
|
|
33150
33660
|
null,
|
|
33151
33661
|
cfg
|
|
@@ -33177,7 +33687,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33177
33687
|
compiled,
|
|
33178
33688
|
resolutions,
|
|
33179
33689
|
projectDir,
|
|
33180
|
-
|
|
33690
|
+
join33(workDir, "downloads")
|
|
33181
33691
|
);
|
|
33182
33692
|
assertNotAborted();
|
|
33183
33693
|
composition.videos = compiled.videos;
|
|
@@ -33204,10 +33714,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33204
33714
|
if (existing.src !== src) {
|
|
33205
33715
|
existing.src = src;
|
|
33206
33716
|
}
|
|
33207
|
-
|
|
33208
|
-
existing.
|
|
33717
|
+
const projectedEnd = projectBrowserEndToCompositionTimeline(
|
|
33718
|
+
existing.start,
|
|
33719
|
+
el.start,
|
|
33720
|
+
el.end
|
|
33721
|
+
);
|
|
33722
|
+
if (projectedEnd > 0 && (existing.end <= 0 || Math.abs(existing.end - projectedEnd) > BROWSER_MEDIA_EPSILON)) {
|
|
33723
|
+
existing.end = projectedEnd;
|
|
33209
33724
|
}
|
|
33210
|
-
if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) >
|
|
33725
|
+
if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) > BROWSER_MEDIA_EPSILON)) {
|
|
33211
33726
|
existing.mediaStart = el.mediaStart;
|
|
33212
33727
|
}
|
|
33213
33728
|
if (el.hasAudio && !existing.hasAudio) {
|
|
@@ -33232,13 +33747,18 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33232
33747
|
if (existing.src !== src) {
|
|
33233
33748
|
existing.src = src;
|
|
33234
33749
|
}
|
|
33235
|
-
|
|
33236
|
-
existing.
|
|
33750
|
+
const projectedEnd = projectBrowserEndToCompositionTimeline(
|
|
33751
|
+
existing.start,
|
|
33752
|
+
el.start,
|
|
33753
|
+
el.end
|
|
33754
|
+
);
|
|
33755
|
+
if (projectedEnd > 0 && (existing.end <= 0 || Math.abs(existing.end - projectedEnd) > BROWSER_MEDIA_EPSILON)) {
|
|
33756
|
+
existing.end = projectedEnd;
|
|
33237
33757
|
}
|
|
33238
|
-
if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) >
|
|
33758
|
+
if (el.mediaStart > 0 && (existing.mediaStart <= 0 || Math.abs(existing.mediaStart - el.mediaStart) > BROWSER_MEDIA_EPSILON)) {
|
|
33239
33759
|
existing.mediaStart = el.mediaStart;
|
|
33240
33760
|
}
|
|
33241
|
-
if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) >
|
|
33761
|
+
if (el.volume > 0 && Math.abs((existing.volume ?? 1) - el.volume) > BROWSER_MEDIA_EPSILON) {
|
|
33242
33762
|
existing.volume = el.volume;
|
|
33243
33763
|
}
|
|
33244
33764
|
}
|
|
@@ -33317,7 +33837,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33317
33837
|
const stage2Start = Date.now();
|
|
33318
33838
|
updateJobStatus(job, "preprocessing", "Extracting video frames", 10, onProgress);
|
|
33319
33839
|
let frameLookup = null;
|
|
33320
|
-
const compiledDir =
|
|
33840
|
+
const compiledDir = join33(workDir, "compiled");
|
|
33321
33841
|
let extractionResult = null;
|
|
33322
33842
|
const nativeHdrVideoIds = /* @__PURE__ */ new Set();
|
|
33323
33843
|
const videoTransfers = /* @__PURE__ */ new Map();
|
|
@@ -33326,10 +33846,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33326
33846
|
composition.videos.map(async (v) => {
|
|
33327
33847
|
let videoPath = v.src;
|
|
33328
33848
|
if (!videoPath.startsWith("/")) {
|
|
33329
|
-
const fromCompiled =
|
|
33849
|
+
const fromCompiled = existsSync31(join33(compiledDir, videoPath)) ? join33(compiledDir, videoPath) : join33(projectDir, videoPath);
|
|
33330
33850
|
videoPath = fromCompiled;
|
|
33331
33851
|
}
|
|
33332
|
-
if (!
|
|
33852
|
+
if (!existsSync31(videoPath)) return;
|
|
33333
33853
|
const meta = await extractMediaMetadata(videoPath);
|
|
33334
33854
|
if (isHdrColorSpace(meta.colorSpace)) {
|
|
33335
33855
|
nativeHdrVideoIds.add(v.id);
|
|
@@ -33347,10 +33867,10 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33347
33867
|
composition.images.map(async (img) => {
|
|
33348
33868
|
let imgPath = img.src;
|
|
33349
33869
|
if (!imgPath.startsWith("/")) {
|
|
33350
|
-
const fromCompiled =
|
|
33870
|
+
const fromCompiled = existsSync31(join33(compiledDir, imgPath)) ? join33(compiledDir, imgPath) : join33(projectDir, imgPath);
|
|
33351
33871
|
imgPath = fromCompiled;
|
|
33352
33872
|
}
|
|
33353
|
-
if (!
|
|
33873
|
+
if (!existsSync31(imgPath)) return null;
|
|
33354
33874
|
const meta = await extractMediaMetadata(imgPath);
|
|
33355
33875
|
if (isHdrColorSpace(meta.colorSpace)) {
|
|
33356
33876
|
nativeHdrImageIds.add(img.id);
|
|
@@ -33366,9 +33886,9 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33366
33886
|
extractionResult = await extractAllVideoFrames(
|
|
33367
33887
|
composition.videos,
|
|
33368
33888
|
projectDir,
|
|
33369
|
-
{ fps: job.config.fps, outputDir:
|
|
33889
|
+
{ fps: job.config.fps, outputDir: join33(workDir, "video-frames") },
|
|
33370
33890
|
abortSignal,
|
|
33371
|
-
|
|
33891
|
+
{ extractCacheDir: cfg.extractCacheDir },
|
|
33372
33892
|
compiledDir
|
|
33373
33893
|
);
|
|
33374
33894
|
assertNotAborted();
|
|
@@ -33424,13 +33944,13 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33424
33944
|
}
|
|
33425
33945
|
const stage3Start = Date.now();
|
|
33426
33946
|
updateJobStatus(job, "preprocessing", "Processing audio tracks", 20, onProgress);
|
|
33427
|
-
const audioOutputPath =
|
|
33947
|
+
const audioOutputPath = join33(workDir, "audio.aac");
|
|
33428
33948
|
let hasAudio = false;
|
|
33429
33949
|
if (composition.audios.length > 0) {
|
|
33430
33950
|
const audioResult = await processCompositionAudio(
|
|
33431
33951
|
composition.audios,
|
|
33432
33952
|
projectDir,
|
|
33433
|
-
|
|
33953
|
+
join33(workDir, "audio-work"),
|
|
33434
33954
|
audioOutputPath,
|
|
33435
33955
|
job.duration,
|
|
33436
33956
|
abortSignal,
|
|
@@ -33448,14 +33968,14 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33448
33968
|
if (!fileServer) {
|
|
33449
33969
|
fileServer = await createFileServer2({
|
|
33450
33970
|
projectDir,
|
|
33451
|
-
compiledDir:
|
|
33971
|
+
compiledDir: join33(workDir, "compiled"),
|
|
33452
33972
|
port: 0,
|
|
33453
33973
|
preHeadScripts: [VIRTUAL_TIME_SHIM]
|
|
33454
33974
|
});
|
|
33455
33975
|
assertNotAborted();
|
|
33456
33976
|
}
|
|
33457
|
-
const framesDir =
|
|
33458
|
-
if (!
|
|
33977
|
+
const framesDir = join33(workDir, "captured-frames");
|
|
33978
|
+
if (!existsSync31(framesDir)) mkdirSync18(framesDir, { recursive: true });
|
|
33459
33979
|
const captureOptions = {
|
|
33460
33980
|
width,
|
|
33461
33981
|
height,
|
|
@@ -33470,7 +33990,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33470
33990
|
const workerCount = calculateOptimalWorkers(totalFrames, job.config.workers, cfg);
|
|
33471
33991
|
const FORMAT_EXT2 = { mp4: ".mp4", webm: ".webm", mov: ".mov" };
|
|
33472
33992
|
const videoExt = FORMAT_EXT2[outputFormat] ?? ".mp4";
|
|
33473
|
-
const videoOnlyPath =
|
|
33993
|
+
const videoOnlyPath = join33(workDir, `video-only${videoExt}`);
|
|
33474
33994
|
const nativeHdrIds = /* @__PURE__ */ new Set([...nativeHdrVideoIds, ...nativeHdrImageIds]);
|
|
33475
33995
|
const hasHdrContent = effectiveHdr && nativeHdrIds.size > 0;
|
|
33476
33996
|
const encoderHdr = hasHdrContent ? effectiveHdr : void 0;
|
|
@@ -33492,8 +34012,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33492
34012
|
if (!hdrVideoIds.includes(v.id)) continue;
|
|
33493
34013
|
let srcPath = v.src;
|
|
33494
34014
|
if (!srcPath.startsWith("/")) {
|
|
33495
|
-
const fromCompiled =
|
|
33496
|
-
srcPath =
|
|
34015
|
+
const fromCompiled = join33(compiledDir, srcPath);
|
|
34016
|
+
srcPath = existsSync31(fromCompiled) ? fromCompiled : join33(projectDir, srcPath);
|
|
33497
34017
|
}
|
|
33498
34018
|
hdrVideoSrcPaths.set(v.id, srcPath);
|
|
33499
34019
|
}
|
|
@@ -33623,8 +34143,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33623
34143
|
for (const [videoId, srcPath] of hdrVideoSrcPaths) {
|
|
33624
34144
|
const video = composition.videos.find((v) => v.id === videoId);
|
|
33625
34145
|
if (!video) continue;
|
|
33626
|
-
const frameDir =
|
|
33627
|
-
|
|
34146
|
+
const frameDir = join33(framesDir, `hdr_${videoId}`);
|
|
34147
|
+
mkdirSync18(frameDir, { recursive: true });
|
|
33628
34148
|
const duration = video.end - video.start;
|
|
33629
34149
|
const dims = hdrExtractionDims.get(videoId) ?? { width, height };
|
|
33630
34150
|
const ffmpegArgs = [
|
|
@@ -33643,7 +34163,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33643
34163
|
"-c:v",
|
|
33644
34164
|
"png",
|
|
33645
34165
|
"-y",
|
|
33646
|
-
|
|
34166
|
+
join33(frameDir, "frame_%04d.png")
|
|
33647
34167
|
];
|
|
33648
34168
|
const result = await runFfmpeg(ffmpegArgs, { signal: abortSignal });
|
|
33649
34169
|
if (!result.success) {
|
|
@@ -33711,15 +34231,19 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33711
34231
|
}
|
|
33712
34232
|
}
|
|
33713
34233
|
const debugDumpEnabled = process.env.KEEP_TEMP === "1";
|
|
33714
|
-
const debugDumpDir = debugDumpEnabled ?
|
|
33715
|
-
if (debugDumpDir && !
|
|
33716
|
-
|
|
34234
|
+
const debugDumpDir = debugDumpEnabled ? join33(framesDir, "debug-composite") : null;
|
|
34235
|
+
if (debugDumpDir && !existsSync31(debugDumpDir)) {
|
|
34236
|
+
mkdirSync18(debugDumpDir, { recursive: true });
|
|
33717
34237
|
}
|
|
33718
34238
|
if (!effectiveHdr) {
|
|
33719
34239
|
throw new Error(
|
|
33720
34240
|
"Internal: HDR render path entered without effectiveHdr \u2014 this is a bug."
|
|
33721
34241
|
);
|
|
33722
34242
|
}
|
|
34243
|
+
const hdrCacheMaxBytes = process.env.HDR_TRANSFER_CACHE_MAX_BYTES ? Number(process.env.HDR_TRANSFER_CACHE_MAX_BYTES) : void 0;
|
|
34244
|
+
const hdrImageTransferCache = createHdrImageTransferCache(
|
|
34245
|
+
hdrCacheMaxBytes !== void 0 ? { maxBytes: hdrCacheMaxBytes } : {}
|
|
34246
|
+
);
|
|
33723
34247
|
const hdrCompositeCtx = {
|
|
33724
34248
|
log: log2,
|
|
33725
34249
|
domSession,
|
|
@@ -33730,6 +34254,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33730
34254
|
effectiveHdr,
|
|
33731
34255
|
nativeHdrImageIds,
|
|
33732
34256
|
hdrImageBuffers,
|
|
34257
|
+
hdrImageTransferCache,
|
|
33733
34258
|
hdrFrameDirs,
|
|
33734
34259
|
hdrVideoStartTimes,
|
|
33735
34260
|
imageTransfers,
|
|
@@ -33790,6 +34315,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33790
34315
|
sceneBuf,
|
|
33791
34316
|
el,
|
|
33792
34317
|
hdrImageBuffers,
|
|
34318
|
+
hdrImageTransferCache,
|
|
33793
34319
|
width,
|
|
33794
34320
|
height,
|
|
33795
34321
|
log2,
|
|
@@ -33853,11 +34379,11 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33853
34379
|
i2
|
|
33854
34380
|
);
|
|
33855
34381
|
if (debugDumpEnabled && debugDumpDir && i2 % 30 === 0) {
|
|
33856
|
-
const previewPath =
|
|
34382
|
+
const previewPath = join33(
|
|
33857
34383
|
debugDumpDir,
|
|
33858
34384
|
`frame_${String(i2).padStart(4, "0")}_final_rgb48le.bin`
|
|
33859
34385
|
);
|
|
33860
|
-
|
|
34386
|
+
writeFileSync12(previewPath, normalCanvas);
|
|
33861
34387
|
}
|
|
33862
34388
|
hdrEncoder.writeFrame(normalCanvas);
|
|
33863
34389
|
}
|
|
@@ -33877,7 +34403,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33877
34403
|
error: err instanceof Error ? err.message : String(err)
|
|
33878
34404
|
});
|
|
33879
34405
|
}
|
|
33880
|
-
|
|
34406
|
+
clearMaxFrameIndex(frameDir);
|
|
33881
34407
|
hdrFrameDirs.delete(videoId);
|
|
33882
34408
|
}
|
|
33883
34409
|
cleanedUpVideos.add(videoId);
|
|
@@ -33928,7 +34454,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
33928
34454
|
});
|
|
33929
34455
|
}
|
|
33930
34456
|
for (const frameDir of hdrFrameDirs.values()) {
|
|
33931
|
-
|
|
34457
|
+
clearMaxFrameIndex(frameDir);
|
|
33932
34458
|
}
|
|
33933
34459
|
hdrFrameDirs.clear();
|
|
33934
34460
|
}
|
|
@@ -34200,6 +34726,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34200
34726
|
updateJobStatus(job, "complete", "Render complete", 100, onProgress);
|
|
34201
34727
|
const totalElapsed = Date.now() - pipelineStart;
|
|
34202
34728
|
sampleMemory();
|
|
34729
|
+
const tmpPeakBytes = existsSync31(workDir) ? sampleDirectoryBytes(workDir) : 0;
|
|
34203
34730
|
const perfSummary = {
|
|
34204
34731
|
renderId: job.id,
|
|
34205
34732
|
totalElapsedMs: totalElapsed,
|
|
@@ -34214,6 +34741,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34214
34741
|
videoCount: composition.videos.length,
|
|
34215
34742
|
audioCount: composition.audios.length,
|
|
34216
34743
|
stages: perfStages,
|
|
34744
|
+
videoExtractBreakdown: extractionResult?.phaseBreakdown,
|
|
34745
|
+
tmpPeakBytes,
|
|
34217
34746
|
hdrDiagnostics: hdrDiagnostics.videoExtractionFailures > 0 || hdrDiagnostics.imageDecodeFailures > 0 ? { ...hdrDiagnostics } : void 0,
|
|
34218
34747
|
captureAvgMs: totalFrames > 0 ? Math.round((perfStages.captureMs ?? 0) / totalFrames) : void 0,
|
|
34219
34748
|
peakRssMb: Math.round(peakRssBytes / (1024 * 1024)),
|
|
@@ -34222,7 +34751,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34222
34751
|
job.perfSummary = perfSummary;
|
|
34223
34752
|
if (job.config.debug) {
|
|
34224
34753
|
try {
|
|
34225
|
-
|
|
34754
|
+
writeFileSync12(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
|
|
34226
34755
|
} catch (err) {
|
|
34227
34756
|
log2.debug("Failed to write perf summary", {
|
|
34228
34757
|
perfOutputPath,
|
|
@@ -34231,8 +34760,8 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34231
34760
|
}
|
|
34232
34761
|
}
|
|
34233
34762
|
if (job.config.debug) {
|
|
34234
|
-
if (
|
|
34235
|
-
const debugOutput =
|
|
34763
|
+
if (existsSync31(outputPath)) {
|
|
34764
|
+
const debugOutput = join33(workDir, `output${videoExt}`);
|
|
34236
34765
|
copyFileSync2(outputPath, debugOutput);
|
|
34237
34766
|
}
|
|
34238
34767
|
} else if (process.env.KEEP_TEMP === "1") {
|
|
@@ -34318,7 +34847,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34318
34847
|
await safeCleanup(
|
|
34319
34848
|
"remove workDir (error)",
|
|
34320
34849
|
() => {
|
|
34321
|
-
if (
|
|
34850
|
+
if (existsSync31(workDir)) rmSync7(workDir, { recursive: true, force: true });
|
|
34322
34851
|
},
|
|
34323
34852
|
log2
|
|
34324
34853
|
);
|
|
@@ -34329,7 +34858,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
|
|
|
34329
34858
|
clearInterval(memSamplerInterval);
|
|
34330
34859
|
}
|
|
34331
34860
|
}
|
|
34332
|
-
var
|
|
34861
|
+
var RenderCancelledError, BROWSER_MEDIA_EPSILON;
|
|
34333
34862
|
var init_renderOrchestrator = __esm({
|
|
34334
34863
|
"../producer/src/services/renderOrchestrator.ts"() {
|
|
34335
34864
|
"use strict";
|
|
@@ -34339,8 +34868,8 @@ var init_renderOrchestrator = __esm({
|
|
|
34339
34868
|
init_htmlCompiler2();
|
|
34340
34869
|
init_logger();
|
|
34341
34870
|
init_paths();
|
|
34342
|
-
|
|
34343
|
-
|
|
34871
|
+
init_frameDirCache();
|
|
34872
|
+
init_hdrImageTransferCache();
|
|
34344
34873
|
RenderCancelledError = class extends Error {
|
|
34345
34874
|
reason;
|
|
34346
34875
|
constructor(message = "render_cancelled", reason = "aborted") {
|
|
@@ -34349,6 +34878,7 @@ var init_renderOrchestrator = __esm({
|
|
|
34349
34878
|
this.reason = reason;
|
|
34350
34879
|
}
|
|
34351
34880
|
};
|
|
34881
|
+
BROWSER_MEDIA_EPSILON = 1e-4;
|
|
34352
34882
|
}
|
|
34353
34883
|
});
|
|
34354
34884
|
|
|
@@ -34377,8 +34907,8 @@ var init_config3 = __esm({
|
|
|
34377
34907
|
});
|
|
34378
34908
|
|
|
34379
34909
|
// ../producer/src/services/hyperframeLint.ts
|
|
34380
|
-
import { existsSync as
|
|
34381
|
-
import { resolve as resolve18, join as
|
|
34910
|
+
import { existsSync as existsSync32, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
|
|
34911
|
+
import { resolve as resolve18, join as join34 } from "path";
|
|
34382
34912
|
function isStringRecord(value) {
|
|
34383
34913
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
34384
34914
|
return false;
|
|
@@ -34406,7 +34936,7 @@ function pickEntryFile(files, preferredEntryFile) {
|
|
|
34406
34936
|
}
|
|
34407
34937
|
function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
34408
34938
|
const absProjectDir = resolve18(projectDir);
|
|
34409
|
-
if (!
|
|
34939
|
+
if (!existsSync32(absProjectDir) || !statSync10(absProjectDir).isDirectory()) {
|
|
34410
34940
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
34411
34941
|
}
|
|
34412
34942
|
const entryCandidates = [preferredEntryFile, "index.html", "src/index.html"].filter(
|
|
@@ -34417,7 +34947,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
34417
34947
|
if (!absoluteEntryPath.startsWith(absProjectDir)) {
|
|
34418
34948
|
return { error: `Entry file must stay inside project directory: ${entryFile}` };
|
|
34419
34949
|
}
|
|
34420
|
-
if (
|
|
34950
|
+
if (existsSync32(absoluteEntryPath) && statSync10(absoluteEntryPath).isFile()) {
|
|
34421
34951
|
return {
|
|
34422
34952
|
entryFile,
|
|
34423
34953
|
html: readFileSync23(absoluteEntryPath, "utf-8"),
|
|
@@ -34426,7 +34956,7 @@ function readProjectEntryFile(projectDir, preferredEntryFile) {
|
|
|
34426
34956
|
}
|
|
34427
34957
|
}
|
|
34428
34958
|
return {
|
|
34429
|
-
error: `No HTML entry file found in project directory: ${
|
|
34959
|
+
error: `No HTML entry file found in project directory: ${join34(absProjectDir, preferredEntryFile || "index.html")}`
|
|
34430
34960
|
};
|
|
34431
34961
|
}
|
|
34432
34962
|
function prepareHyperframeLintBody(body) {
|
|
@@ -34514,15 +35044,15 @@ var init_semaphore = __esm({
|
|
|
34514
35044
|
|
|
34515
35045
|
// ../producer/src/server.ts
|
|
34516
35046
|
import {
|
|
34517
|
-
existsSync as
|
|
34518
|
-
mkdirSync as
|
|
34519
|
-
statSync as
|
|
35047
|
+
existsSync as existsSync33,
|
|
35048
|
+
mkdirSync as mkdirSync19,
|
|
35049
|
+
statSync as statSync11,
|
|
34520
35050
|
mkdtempSync as mkdtempSync2,
|
|
34521
|
-
writeFileSync as
|
|
35051
|
+
writeFileSync as writeFileSync13,
|
|
34522
35052
|
rmSync as rmSync8,
|
|
34523
35053
|
createReadStream
|
|
34524
35054
|
} from "fs";
|
|
34525
|
-
import { resolve as resolve19, dirname as dirname12, join as
|
|
35055
|
+
import { resolve as resolve19, dirname as dirname12, join as join35 } from "path";
|
|
34526
35056
|
import { tmpdir as tmpdir3 } from "os";
|
|
34527
35057
|
import { parseArgs as parseArgs2 } from "util";
|
|
34528
35058
|
import crypto2 from "crypto";
|
|
@@ -34545,11 +35075,11 @@ async function prepareRenderBody(body) {
|
|
|
34545
35075
|
const projectDir = typeof body.projectDir === "string" ? body.projectDir : void 0;
|
|
34546
35076
|
if (projectDir) {
|
|
34547
35077
|
const absProjectDir = resolve19(projectDir);
|
|
34548
|
-
if (!
|
|
35078
|
+
if (!existsSync33(absProjectDir) || !statSync11(absProjectDir).isDirectory()) {
|
|
34549
35079
|
return { error: `Project directory not found: ${absProjectDir}` };
|
|
34550
35080
|
}
|
|
34551
35081
|
const entry = options.entryFile || "index.html";
|
|
34552
|
-
if (!
|
|
35082
|
+
if (!existsSync33(resolve19(absProjectDir, entry))) {
|
|
34553
35083
|
return { error: `Entry file "${entry}" not found in project directory: ${absProjectDir}` };
|
|
34554
35084
|
}
|
|
34555
35085
|
return { prepared: { input: { projectDir: absProjectDir, ...options } } };
|
|
@@ -34574,8 +35104,8 @@ async function prepareRenderBody(body) {
|
|
|
34574
35104
|
}
|
|
34575
35105
|
}
|
|
34576
35106
|
const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir3();
|
|
34577
|
-
const tempProjectDir = mkdtempSync2(
|
|
34578
|
-
|
|
35107
|
+
const tempProjectDir = mkdtempSync2(join35(tempRoot, "producer-project-"));
|
|
35108
|
+
writeFileSync13(join35(tempProjectDir, "index.html"), htmlContent, "utf-8");
|
|
34579
35109
|
return {
|
|
34580
35110
|
prepared: {
|
|
34581
35111
|
input: {
|
|
@@ -34698,7 +35228,7 @@ function createRenderHandlers(options = {}) {
|
|
|
34698
35228
|
log2
|
|
34699
35229
|
);
|
|
34700
35230
|
const outputDir = dirname12(absoluteOutputPath);
|
|
34701
|
-
if (!
|
|
35231
|
+
if (!existsSync33(outputDir)) mkdirSync19(outputDir, { recursive: true });
|
|
34702
35232
|
const release2 = await renderSemaphore.acquire();
|
|
34703
35233
|
log2.info("render started", {
|
|
34704
35234
|
requestId,
|
|
@@ -34725,7 +35255,7 @@ function createRenderHandlers(options = {}) {
|
|
|
34725
35255
|
log2.info(`render progress ${pct}%`, { requestId, stage: j2.currentStage, message });
|
|
34726
35256
|
}
|
|
34727
35257
|
});
|
|
34728
|
-
const fileSize =
|
|
35258
|
+
const fileSize = existsSync33(absoluteOutputPath) ? statSync11(absoluteOutputPath).size : 0;
|
|
34729
35259
|
const durationMs = Date.now() - t0;
|
|
34730
35260
|
const outputToken = store.register(absoluteOutputPath);
|
|
34731
35261
|
const outputUrl = `${outputUrlPrefix}/${outputToken}`;
|
|
@@ -34809,7 +35339,7 @@ function createRenderHandlers(options = {}) {
|
|
|
34809
35339
|
log2
|
|
34810
35340
|
);
|
|
34811
35341
|
const outputDir = dirname12(absoluteOutputPath);
|
|
34812
|
-
if (!
|
|
35342
|
+
if (!existsSync33(outputDir)) mkdirSync19(outputDir, { recursive: true });
|
|
34813
35343
|
log2.info("render-stream started", { requestId, projectDir: input.projectDir });
|
|
34814
35344
|
const job = createRenderJob({
|
|
34815
35345
|
fps: input.fps,
|
|
@@ -34854,7 +35384,7 @@ function createRenderHandlers(options = {}) {
|
|
|
34854
35384
|
},
|
|
34855
35385
|
abortController.signal
|
|
34856
35386
|
);
|
|
34857
|
-
const fileSize =
|
|
35387
|
+
const fileSize = existsSync33(absoluteOutputPath) ? statSync11(absoluteOutputPath).size : 0;
|
|
34858
35388
|
const outputToken = store.register(absoluteOutputPath);
|
|
34859
35389
|
const outputUrl = `${outputUrlPrefix}/${outputToken}`;
|
|
34860
35390
|
log2.info("render-stream completed", { requestId, fileSize, perf: job.perfSummary ?? null });
|
|
@@ -34913,11 +35443,11 @@ function createRenderHandlers(options = {}) {
|
|
|
34913
35443
|
if (!artifact) {
|
|
34914
35444
|
return c2.json({ success: false, error: "Output artifact not found or expired" }, 404);
|
|
34915
35445
|
}
|
|
34916
|
-
if (!
|
|
35446
|
+
if (!existsSync33(artifact.path)) {
|
|
34917
35447
|
store.delete(token);
|
|
34918
35448
|
return c2.json({ success: false, error: "Output artifact file missing" }, 404);
|
|
34919
35449
|
}
|
|
34920
|
-
const stats =
|
|
35450
|
+
const stats = statSync11(artifact.path);
|
|
34921
35451
|
return new Response(createReadStream(artifact.path), {
|
|
34922
35452
|
headers: {
|
|
34923
35453
|
"content-type": "video/mp4",
|
|
@@ -35050,20 +35580,20 @@ __export(studioServer_exports, {
|
|
|
35050
35580
|
});
|
|
35051
35581
|
import { Hono as Hono5 } from "hono";
|
|
35052
35582
|
import { streamSSE as streamSSE3 } from "hono/streaming";
|
|
35053
|
-
import { existsSync as
|
|
35054
|
-
import { resolve as resolve20, join as
|
|
35583
|
+
import { existsSync as existsSync34, readFileSync as readFileSync24, writeFileSync as writeFileSync14, statSync as statSync12 } from "fs";
|
|
35584
|
+
import { resolve as resolve20, join as join36, basename as basename3 } from "path";
|
|
35055
35585
|
function resolveDistDir() {
|
|
35056
35586
|
const builtPath = resolve20(__dirname, "studio");
|
|
35057
|
-
if (
|
|
35587
|
+
if (existsSync34(resolve20(builtPath, "index.html"))) return builtPath;
|
|
35058
35588
|
const devPath = resolve20(__dirname, "..", "..", "..", "studio", "dist");
|
|
35059
|
-
if (
|
|
35589
|
+
if (existsSync34(resolve20(devPath, "index.html"))) return devPath;
|
|
35060
35590
|
return builtPath;
|
|
35061
35591
|
}
|
|
35062
35592
|
function resolveRuntimePath() {
|
|
35063
35593
|
const builtPath = resolve20(__dirname, "hyperframe-runtime.js");
|
|
35064
|
-
if (
|
|
35594
|
+
if (existsSync34(builtPath)) return builtPath;
|
|
35065
35595
|
const iifePath = resolve20(__dirname, "hyperframe.runtime.iife.js");
|
|
35066
|
-
if (
|
|
35596
|
+
if (existsSync34(iifePath)) return iifePath;
|
|
35067
35597
|
const devPath = resolve20(
|
|
35068
35598
|
__dirname,
|
|
35069
35599
|
"..",
|
|
@@ -35073,7 +35603,7 @@ function resolveRuntimePath() {
|
|
|
35073
35603
|
"dist",
|
|
35074
35604
|
"hyperframe.runtime.iife.js"
|
|
35075
35605
|
);
|
|
35076
|
-
if (
|
|
35606
|
+
if (existsSync34(devPath)) return devPath;
|
|
35077
35607
|
return builtPath;
|
|
35078
35608
|
}
|
|
35079
35609
|
async function getThumbnailBrowser() {
|
|
@@ -35135,7 +35665,7 @@ function createStudioServer(options) {
|
|
|
35135
35665
|
return lintHyperframeHtml2(html, opts);
|
|
35136
35666
|
},
|
|
35137
35667
|
runtimeUrl: "/api/runtime.js",
|
|
35138
|
-
rendersDir: () =>
|
|
35668
|
+
rendersDir: () => join36(projectDir, "renders"),
|
|
35139
35669
|
startRender(opts) {
|
|
35140
35670
|
const state = {
|
|
35141
35671
|
id: opts.jobId,
|
|
@@ -35168,7 +35698,7 @@ function createStudioServer(options) {
|
|
|
35168
35698
|
state.status = "complete";
|
|
35169
35699
|
state.progress = 100;
|
|
35170
35700
|
const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
|
|
35171
|
-
|
|
35701
|
+
writeFileSync14(
|
|
35172
35702
|
metaPath,
|
|
35173
35703
|
JSON.stringify({ status: "complete", durationMs: Date.now() - startTime })
|
|
35174
35704
|
);
|
|
@@ -35177,7 +35707,7 @@ function createStudioServer(options) {
|
|
|
35177
35707
|
state.error = err instanceof Error ? err.message : String(err);
|
|
35178
35708
|
try {
|
|
35179
35709
|
const metaPath = opts.outputPath.replace(/\.(mp4|webm|mov)$/, ".meta.json");
|
|
35180
|
-
|
|
35710
|
+
writeFileSync14(metaPath, JSON.stringify({ status: "failed" }));
|
|
35181
35711
|
} catch {
|
|
35182
35712
|
}
|
|
35183
35713
|
}
|
|
@@ -35250,7 +35780,7 @@ function createStudioServer(options) {
|
|
|
35250
35780
|
});
|
|
35251
35781
|
app.get("/api/runtime.js", (c2) => {
|
|
35252
35782
|
const serve4 = async () => {
|
|
35253
|
-
const runtimeSource = await loadRuntimeSource() ?? (
|
|
35783
|
+
const runtimeSource = await loadRuntimeSource() ?? (existsSync34(runtimePath) ? readFileSync24(runtimePath, "utf-8") : null);
|
|
35254
35784
|
if (!runtimeSource) return c2.text("runtime not available", 404);
|
|
35255
35785
|
return c2.body(runtimeSource, 200, {
|
|
35256
35786
|
"Content-Type": "text/javascript",
|
|
@@ -35286,7 +35816,7 @@ function createStudioServer(options) {
|
|
|
35286
35816
|
});
|
|
35287
35817
|
app.get("/assets/*", (c2) => {
|
|
35288
35818
|
const filePath = resolve20(studioDir, c2.req.path.slice(1));
|
|
35289
|
-
if (!
|
|
35819
|
+
if (!existsSync34(filePath) || !statSync12(filePath).isFile()) return c2.text("not found", 404);
|
|
35290
35820
|
const content = readFileSync24(filePath);
|
|
35291
35821
|
return new Response(content, {
|
|
35292
35822
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
@@ -35294,7 +35824,7 @@ function createStudioServer(options) {
|
|
|
35294
35824
|
});
|
|
35295
35825
|
app.get("/icons/*", (c2) => {
|
|
35296
35826
|
const filePath = resolve20(studioDir, c2.req.path.slice(1));
|
|
35297
|
-
if (!
|
|
35827
|
+
if (!existsSync34(filePath) || !statSync12(filePath).isFile()) return c2.text("not found", 404);
|
|
35298
35828
|
const content = readFileSync24(filePath);
|
|
35299
35829
|
return new Response(content, {
|
|
35300
35830
|
headers: { "Content-Type": getMimeType(filePath), "Cache-Control": "no-store" }
|
|
@@ -35302,7 +35832,7 @@ function createStudioServer(options) {
|
|
|
35302
35832
|
});
|
|
35303
35833
|
app.get("*", (c2) => {
|
|
35304
35834
|
const indexPath = resolve20(studioDir, "index.html");
|
|
35305
|
-
if (!
|
|
35835
|
+
if (!existsSync34(indexPath)) {
|
|
35306
35836
|
return c2.text("Studio not found. Rebuild with: pnpm run build", 500);
|
|
35307
35837
|
}
|
|
35308
35838
|
return c2.html(readFileSync24(indexPath, "utf-8"));
|
|
@@ -35329,20 +35859,20 @@ __export(preview_exports, {
|
|
|
35329
35859
|
examples: () => examples
|
|
35330
35860
|
});
|
|
35331
35861
|
import { spawn as spawn8 } from "child_process";
|
|
35332
|
-
import { existsSync as
|
|
35333
|
-
import { resolve as resolve21, dirname as dirname13, basename as basename4, join as
|
|
35862
|
+
import { existsSync as existsSync35, lstatSync, symlinkSync, unlinkSync as unlinkSync5, readlinkSync, mkdirSync as mkdirSync20 } from "fs";
|
|
35863
|
+
import { resolve as resolve21, dirname as dirname13, basename as basename4, join as join37 } from "path";
|
|
35334
35864
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
35335
35865
|
import { createRequire } from "module";
|
|
35336
35866
|
async function runDevMode(dir, projectName) {
|
|
35337
35867
|
const thisFile = fileURLToPath4(import.meta.url);
|
|
35338
35868
|
const repoRoot = resolve21(dirname13(thisFile), "..", "..", "..", "..");
|
|
35339
|
-
const projectsDir =
|
|
35869
|
+
const projectsDir = join37(repoRoot, "packages", "studio", "data", "projects");
|
|
35340
35870
|
const pName = projectName ?? basename4(dir);
|
|
35341
|
-
const symlinkPath =
|
|
35342
|
-
|
|
35871
|
+
const symlinkPath = join37(projectsDir, pName);
|
|
35872
|
+
mkdirSync20(projectsDir, { recursive: true });
|
|
35343
35873
|
let createdSymlink = false;
|
|
35344
35874
|
if (dir !== symlinkPath) {
|
|
35345
|
-
if (
|
|
35875
|
+
if (existsSync35(symlinkPath)) {
|
|
35346
35876
|
try {
|
|
35347
35877
|
const stat3 = lstatSync(symlinkPath);
|
|
35348
35878
|
if (stat3.isSymbolicLink()) {
|
|
@@ -35354,7 +35884,7 @@ async function runDevMode(dir, projectName) {
|
|
|
35354
35884
|
} catch {
|
|
35355
35885
|
}
|
|
35356
35886
|
}
|
|
35357
|
-
if (!
|
|
35887
|
+
if (!existsSync35(symlinkPath)) {
|
|
35358
35888
|
symlinkSync(dir, symlinkPath, "dir");
|
|
35359
35889
|
createdSymlink = true;
|
|
35360
35890
|
}
|
|
@@ -35362,7 +35892,7 @@ async function runDevMode(dir, projectName) {
|
|
|
35362
35892
|
Wt2(c.bold("hyperframes preview"));
|
|
35363
35893
|
const s2 = be();
|
|
35364
35894
|
s2.start("Starting studio...");
|
|
35365
|
-
const studioPkgDir =
|
|
35895
|
+
const studioPkgDir = join37(repoRoot, "packages", "studio");
|
|
35366
35896
|
const child = spawn8("pnpm", ["exec", "vite"], {
|
|
35367
35897
|
cwd: studioPkgDir,
|
|
35368
35898
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -35396,7 +35926,7 @@ async function runDevMode(dir, projectName) {
|
|
|
35396
35926
|
if (createdSymlink) {
|
|
35397
35927
|
process.on("exit", () => {
|
|
35398
35928
|
try {
|
|
35399
|
-
if (
|
|
35929
|
+
if (existsSync35(symlinkPath)) unlinkSync5(symlinkPath);
|
|
35400
35930
|
} catch {
|
|
35401
35931
|
}
|
|
35402
35932
|
});
|
|
@@ -35407,7 +35937,7 @@ async function runDevMode(dir, projectName) {
|
|
|
35407
35937
|
}
|
|
35408
35938
|
function hasLocalStudio(dir) {
|
|
35409
35939
|
try {
|
|
35410
|
-
const req = createRequire(
|
|
35940
|
+
const req = createRequire(join37(dir, "package.json"));
|
|
35411
35941
|
req.resolve("@hyperframes/studio/package.json");
|
|
35412
35942
|
return true;
|
|
35413
35943
|
} catch {
|
|
@@ -35415,20 +35945,20 @@ function hasLocalStudio(dir) {
|
|
|
35415
35945
|
}
|
|
35416
35946
|
}
|
|
35417
35947
|
async function runLocalStudioMode(dir, projectName) {
|
|
35418
|
-
const req = createRequire(
|
|
35948
|
+
const req = createRequire(join37(dir, "package.json"));
|
|
35419
35949
|
const studioPkgPath = dirname13(req.resolve("@hyperframes/studio/package.json"));
|
|
35420
35950
|
const pName = projectName ?? basename4(dir);
|
|
35421
|
-
const projectsDir =
|
|
35422
|
-
const symlinkPath =
|
|
35423
|
-
|
|
35951
|
+
const projectsDir = join37(studioPkgPath, "data", "projects");
|
|
35952
|
+
const symlinkPath = join37(projectsDir, pName);
|
|
35953
|
+
mkdirSync20(projectsDir, { recursive: true });
|
|
35424
35954
|
let createdSymlink = false;
|
|
35425
35955
|
if (dir !== symlinkPath) {
|
|
35426
|
-
if (
|
|
35956
|
+
if (existsSync35(symlinkPath) && lstatSync(symlinkPath).isSymbolicLink()) {
|
|
35427
35957
|
if (resolve21(readlinkSync(symlinkPath)) !== resolve21(dir)) {
|
|
35428
35958
|
unlinkSync5(symlinkPath);
|
|
35429
35959
|
}
|
|
35430
35960
|
}
|
|
35431
|
-
if (!
|
|
35961
|
+
if (!existsSync35(symlinkPath)) {
|
|
35432
35962
|
symlinkSync(dir, symlinkPath, "dir");
|
|
35433
35963
|
createdSymlink = true;
|
|
35434
35964
|
}
|
|
@@ -35467,7 +35997,7 @@ async function runLocalStudioMode(dir, projectName) {
|
|
|
35467
35997
|
if (createdSymlink) {
|
|
35468
35998
|
process.on("exit", () => {
|
|
35469
35999
|
try {
|
|
35470
|
-
if (
|
|
36000
|
+
if (existsSync35(symlinkPath)) unlinkSync5(symlinkPath);
|
|
35471
36001
|
} catch {
|
|
35472
36002
|
}
|
|
35473
36003
|
});
|
|
@@ -35607,8 +36137,8 @@ var init_preview2 = __esm({
|
|
|
35607
36137
|
const dir = resolve21(rawArg ?? ".");
|
|
35608
36138
|
const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
|
|
35609
36139
|
const projectName = isImplicitCwd ? basename4(process.env.PWD ?? dir) : basename4(dir);
|
|
35610
|
-
const indexPath =
|
|
35611
|
-
if (
|
|
36140
|
+
const indexPath = join37(dir, "index.html");
|
|
36141
|
+
if (existsSync35(indexPath)) {
|
|
35612
36142
|
const project = { dir, name: projectName, indexPath };
|
|
35613
36143
|
const lintResult = lintProject(project);
|
|
35614
36144
|
if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
|
|
@@ -35637,15 +36167,15 @@ __export(init_exports, {
|
|
|
35637
36167
|
examples: () => examples2
|
|
35638
36168
|
});
|
|
35639
36169
|
import {
|
|
35640
|
-
existsSync as
|
|
35641
|
-
mkdirSync as
|
|
36170
|
+
existsSync as existsSync36,
|
|
36171
|
+
mkdirSync as mkdirSync21,
|
|
35642
36172
|
copyFileSync as copyFileSync3,
|
|
35643
36173
|
cpSync,
|
|
35644
|
-
writeFileSync as
|
|
36174
|
+
writeFileSync as writeFileSync15,
|
|
35645
36175
|
readFileSync as readFileSync25,
|
|
35646
|
-
readdirSync as
|
|
36176
|
+
readdirSync as readdirSync14
|
|
35647
36177
|
} from "fs";
|
|
35648
|
-
import { resolve as resolve22, basename as basename5, join as
|
|
36178
|
+
import { resolve as resolve22, basename as basename5, join as join38, dirname as dirname14 } from "path";
|
|
35649
36179
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
35650
36180
|
import { execFileSync as execFileSync4, spawn as spawn9 } from "child_process";
|
|
35651
36181
|
function probeVideo(filePath) {
|
|
@@ -35717,7 +36247,7 @@ function resolveAssetDir(devSegments, builtSegments) {
|
|
|
35717
36247
|
const base = dirname14(fileURLToPath5(import.meta.url));
|
|
35718
36248
|
const devPath = resolve22(base, ...devSegments);
|
|
35719
36249
|
const builtPath = resolve22(base, ...builtSegments);
|
|
35720
|
-
return
|
|
36250
|
+
return existsSync36(devPath) ? devPath : builtPath;
|
|
35721
36251
|
}
|
|
35722
36252
|
function getStaticTemplateDir(templateId) {
|
|
35723
36253
|
return resolveAssetDir(["..", "templates", templateId], ["templates", templateId]);
|
|
@@ -35726,7 +36256,7 @@ function getSharedTemplateDir() {
|
|
|
35726
36256
|
return resolveAssetDir(["..", "templates", "_shared"], ["templates", "_shared"]);
|
|
35727
36257
|
}
|
|
35728
36258
|
function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
35729
|
-
const htmlFiles =
|
|
36259
|
+
const htmlFiles = readdirSync14(dir, { withFileTypes: true, recursive: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".html")).map((e2) => join38(e2.parentPath ?? e2.path, e2.name));
|
|
35730
36260
|
for (const file of htmlFiles) {
|
|
35731
36261
|
let content = readFileSync25(file, "utf-8");
|
|
35732
36262
|
if (videoFilename) {
|
|
@@ -35739,7 +36269,7 @@ function patchVideoSrc(dir, videoFilename, durationSeconds) {
|
|
|
35739
36269
|
}
|
|
35740
36270
|
const dur = durationSeconds ? String(Math.round(durationSeconds * 100) / 100) : "10";
|
|
35741
36271
|
content = content.replaceAll("__VIDEO_DURATION__", dur);
|
|
35742
|
-
|
|
36272
|
+
writeFileSync15(file, content, "utf-8");
|
|
35743
36273
|
}
|
|
35744
36274
|
}
|
|
35745
36275
|
async function patchTranscript(dir, transcriptPath) {
|
|
@@ -35831,15 +36361,15 @@ async function handleVideoFile(videoPath, destDir, interactive) {
|
|
|
35831
36361
|
return { meta, localVideoName };
|
|
35832
36362
|
}
|
|
35833
36363
|
async function scaffoldProject(destDir, name, templateId, localVideoName, durationSeconds) {
|
|
35834
|
-
|
|
36364
|
+
mkdirSync21(destDir, { recursive: true });
|
|
35835
36365
|
const templateDir = getStaticTemplateDir(templateId);
|
|
35836
|
-
if (
|
|
36366
|
+
if (existsSync36(templateDir)) {
|
|
35837
36367
|
cpSync(templateDir, destDir, { recursive: true });
|
|
35838
36368
|
} else {
|
|
35839
36369
|
await fetchRemoteTemplate(templateId, destDir);
|
|
35840
36370
|
}
|
|
35841
36371
|
patchVideoSrc(destDir, localVideoName, durationSeconds);
|
|
35842
|
-
|
|
36372
|
+
writeFileSync15(
|
|
35843
36373
|
resolve22(destDir, "meta.json"),
|
|
35844
36374
|
JSON.stringify(
|
|
35845
36375
|
{
|
|
@@ -35852,14 +36382,14 @@ async function scaffoldProject(destDir, name, templateId, localVideoName, durati
|
|
|
35852
36382
|
),
|
|
35853
36383
|
"utf-8"
|
|
35854
36384
|
);
|
|
35855
|
-
if (!
|
|
36385
|
+
if (!existsSync36(resolve22(destDir, "hyperframes.json"))) {
|
|
35856
36386
|
const { writeProjectConfig: writeProjectConfig2, DEFAULT_PROJECT_CONFIG: DEFAULT_PROJECT_CONFIG2 } = await Promise.resolve().then(() => (init_projectConfig(), projectConfig_exports));
|
|
35857
36387
|
writeProjectConfig2(destDir, DEFAULT_PROJECT_CONFIG2);
|
|
35858
36388
|
}
|
|
35859
36389
|
const sharedDir = getSharedTemplateDir();
|
|
35860
|
-
if (
|
|
35861
|
-
for (const entry of
|
|
35862
|
-
const src =
|
|
36390
|
+
if (existsSync36(sharedDir)) {
|
|
36391
|
+
for (const entry of readdirSync14(sharedDir, { withFileTypes: true })) {
|
|
36392
|
+
const src = join38(sharedDir, entry.name);
|
|
35863
36393
|
const dest = resolve22(destDir, entry.name);
|
|
35864
36394
|
if (entry.isFile() || entry.isSymbolicLink()) {
|
|
35865
36395
|
copyFileSync3(src, dest);
|
|
@@ -35973,11 +36503,11 @@ var init_init = __esm({
|
|
|
35973
36503
|
const templateId2 = exampleFlag ?? "blank";
|
|
35974
36504
|
const name2 = args.name ?? "my-video";
|
|
35975
36505
|
const destDir2 = resolve22(name2);
|
|
35976
|
-
if (
|
|
36506
|
+
if (existsSync36(destDir2) && readdirSync14(destDir2).length > 0) {
|
|
35977
36507
|
console.error(c.error(`Directory already exists and is not empty: ${name2}`));
|
|
35978
36508
|
process.exit(1);
|
|
35979
36509
|
}
|
|
35980
|
-
|
|
36510
|
+
mkdirSync21(destDir2, { recursive: true });
|
|
35981
36511
|
let localVideoName2;
|
|
35982
36512
|
let videoDuration2;
|
|
35983
36513
|
let sourceFilePath2;
|
|
@@ -35987,7 +36517,7 @@ var init_init = __esm({
|
|
|
35987
36517
|
}
|
|
35988
36518
|
if (videoFlag) {
|
|
35989
36519
|
const videoPath = resolve22(videoFlag);
|
|
35990
|
-
if (!
|
|
36520
|
+
if (!existsSync36(videoPath)) {
|
|
35991
36521
|
console.error(c.error(`Video file not found: ${videoFlag}`));
|
|
35992
36522
|
process.exit(1);
|
|
35993
36523
|
}
|
|
@@ -36001,7 +36531,7 @@ var init_init = __esm({
|
|
|
36001
36531
|
}
|
|
36002
36532
|
if (audioFlag) {
|
|
36003
36533
|
const audioPath = resolve22(audioFlag);
|
|
36004
|
-
if (!
|
|
36534
|
+
if (!existsSync36(audioPath)) {
|
|
36005
36535
|
console.error(c.error(`Audio file not found: ${audioFlag}`));
|
|
36006
36536
|
process.exit(1);
|
|
36007
36537
|
}
|
|
@@ -36047,11 +36577,11 @@ var init_init = __esm({
|
|
|
36047
36577
|
}
|
|
36048
36578
|
trackInitTemplate(templateId2);
|
|
36049
36579
|
const transcriptFile2 = resolve22(destDir2, "transcript.json");
|
|
36050
|
-
if (
|
|
36580
|
+
if (existsSync36(transcriptFile2)) {
|
|
36051
36581
|
await patchTranscript(destDir2, transcriptFile2);
|
|
36052
36582
|
}
|
|
36053
36583
|
console.log(c.success(`Created ${c.accent(name2 + "/")}`));
|
|
36054
|
-
for (const f3 of
|
|
36584
|
+
for (const f3 of readdirSync14(destDir2).filter((f4) => !f4.startsWith("."))) {
|
|
36055
36585
|
console.log(` ${c.accent(f3)}`);
|
|
36056
36586
|
}
|
|
36057
36587
|
console.log();
|
|
@@ -36099,7 +36629,7 @@ var init_init = __esm({
|
|
|
36099
36629
|
name = nameResult;
|
|
36100
36630
|
}
|
|
36101
36631
|
const destDir = resolve22(name);
|
|
36102
|
-
if (
|
|
36632
|
+
if (existsSync36(destDir) && readdirSync14(destDir).length > 0) {
|
|
36103
36633
|
const overwrite = await Rt({
|
|
36104
36634
|
message: `Directory ${c.accent(name)} already exists and is not empty. Overwrite?`,
|
|
36105
36635
|
initialValue: false
|
|
@@ -36114,24 +36644,24 @@ var init_init = __esm({
|
|
|
36114
36644
|
let videoDuration;
|
|
36115
36645
|
if (videoFlag) {
|
|
36116
36646
|
const videoPath = resolve22(videoFlag);
|
|
36117
|
-
if (!
|
|
36647
|
+
if (!existsSync36(videoPath)) {
|
|
36118
36648
|
R2.error(`File not found: ${videoFlag}`);
|
|
36119
36649
|
Nt("Setup cancelled.");
|
|
36120
36650
|
process.exit(1);
|
|
36121
36651
|
}
|
|
36122
|
-
|
|
36652
|
+
mkdirSync21(destDir, { recursive: true });
|
|
36123
36653
|
sourceFilePath = videoPath;
|
|
36124
36654
|
const result = await handleVideoFile(videoPath, destDir, true);
|
|
36125
36655
|
localVideoName = result.localVideoName;
|
|
36126
36656
|
videoDuration = result.meta.durationSeconds;
|
|
36127
36657
|
} else if (audioFlag) {
|
|
36128
36658
|
const audioPath = resolve22(audioFlag);
|
|
36129
|
-
if (!
|
|
36659
|
+
if (!existsSync36(audioPath)) {
|
|
36130
36660
|
R2.error(`File not found: ${audioFlag}`);
|
|
36131
36661
|
Nt("Setup cancelled.");
|
|
36132
36662
|
process.exit(1);
|
|
36133
36663
|
}
|
|
36134
|
-
|
|
36664
|
+
mkdirSync21(destDir, { recursive: true });
|
|
36135
36665
|
sourceFilePath = audioPath;
|
|
36136
36666
|
copyFileSync3(audioPath, resolve22(destDir, basename5(audioPath)));
|
|
36137
36667
|
R2.info(`Audio copied to ${c.accent(basename5(audioPath))}`);
|
|
@@ -36219,10 +36749,10 @@ ${c.dim("Use --example blank for offline use.")}`
|
|
|
36219
36749
|
}
|
|
36220
36750
|
trackInitTemplate(templateId);
|
|
36221
36751
|
const transcriptFile = resolve22(destDir, "transcript.json");
|
|
36222
|
-
if (
|
|
36752
|
+
if (existsSync36(transcriptFile)) {
|
|
36223
36753
|
await patchTranscript(destDir, transcriptFile);
|
|
36224
36754
|
}
|
|
36225
|
-
const files =
|
|
36755
|
+
const files = readdirSync14(destDir);
|
|
36226
36756
|
Vt2(files.map((f3) => c.accent(f3)).join("\n"), c.success(`Created ${name}/`));
|
|
36227
36757
|
if (!skipSkills) {
|
|
36228
36758
|
const installSkills = await Rt({
|
|
@@ -36305,7 +36835,7 @@ __export(add_exports, {
|
|
|
36305
36835
|
remapTarget: () => remapTarget,
|
|
36306
36836
|
runAdd: () => runAdd
|
|
36307
36837
|
});
|
|
36308
|
-
import { existsSync as
|
|
36838
|
+
import { existsSync as existsSync37 } from "fs";
|
|
36309
36839
|
import { resolve as resolve23, relative as relative3 } from "path";
|
|
36310
36840
|
function remapTarget(item, originalTarget, paths) {
|
|
36311
36841
|
if (item.type === "hyperframes:block") {
|
|
@@ -36331,8 +36861,8 @@ function buildSnippet(item, relativeTarget) {
|
|
|
36331
36861
|
async function runAdd(opts) {
|
|
36332
36862
|
const projectDir = resolve23(opts.projectDir);
|
|
36333
36863
|
let config = loadProjectConfig(projectDir);
|
|
36334
|
-
const hasConfig =
|
|
36335
|
-
if (!hasConfig &&
|
|
36864
|
+
const hasConfig = existsSync37(projectConfigPath(projectDir));
|
|
36865
|
+
if (!hasConfig && existsSync37(resolve23(projectDir, "index.html"))) {
|
|
36336
36866
|
writeProjectConfig(projectDir, DEFAULT_PROJECT_CONFIG);
|
|
36337
36867
|
config = DEFAULT_PROJECT_CONFIG;
|
|
36338
36868
|
}
|
|
@@ -36431,10 +36961,10 @@ var init_add = __esm({
|
|
|
36431
36961
|
const projectDir = resolve23(args.dir ?? process.cwd());
|
|
36432
36962
|
const json = args.json === true;
|
|
36433
36963
|
const skipClipboard = args["no-clipboard"] === true;
|
|
36434
|
-
const hasConfigBefore =
|
|
36964
|
+
const hasConfigBefore = existsSync37(projectConfigPath(projectDir));
|
|
36435
36965
|
try {
|
|
36436
36966
|
const result = await runAdd({ name: args.name, projectDir, skipClipboard });
|
|
36437
|
-
const wroteConfig = !hasConfigBefore &&
|
|
36967
|
+
const wroteConfig = !hasConfigBefore && existsSync37(projectConfigPath(projectDir));
|
|
36438
36968
|
if (json) {
|
|
36439
36969
|
console.log(JSON.stringify(result));
|
|
36440
36970
|
return;
|
|
@@ -36644,17 +37174,17 @@ var init_format = __esm({
|
|
|
36644
37174
|
});
|
|
36645
37175
|
|
|
36646
37176
|
// src/utils/project.ts
|
|
36647
|
-
import { existsSync as
|
|
37177
|
+
import { existsSync as existsSync38, statSync as statSync13 } from "fs";
|
|
36648
37178
|
import { resolve as resolve25, basename as basename6 } from "path";
|
|
36649
37179
|
function resolveProject(dirArg) {
|
|
36650
37180
|
const dir = resolve25(dirArg ?? ".");
|
|
36651
37181
|
const name = basename6(dir);
|
|
36652
37182
|
const indexPath = resolve25(dir, "index.html");
|
|
36653
|
-
if (!
|
|
37183
|
+
if (!existsSync38(dir) || !statSync13(dir).isDirectory()) {
|
|
36654
37184
|
errorBox("Not a directory: " + dir);
|
|
36655
37185
|
process.exit(1);
|
|
36656
37186
|
}
|
|
36657
|
-
if (!
|
|
37187
|
+
if (!existsSync38(indexPath)) {
|
|
36658
37188
|
errorBox(
|
|
36659
37189
|
"No composition found in " + dir,
|
|
36660
37190
|
"No index.html file found.",
|
|
@@ -36677,7 +37207,7 @@ __export(play_exports, {
|
|
|
36677
37207
|
default: () => play_default,
|
|
36678
37208
|
examples: () => examples5
|
|
36679
37209
|
});
|
|
36680
|
-
import { existsSync as
|
|
37210
|
+
import { existsSync as existsSync39, readFileSync as readFileSync26 } from "fs";
|
|
36681
37211
|
import { resolve as resolve26, dirname as dirname15 } from "path";
|
|
36682
37212
|
function commandDir() {
|
|
36683
37213
|
return dirname15(new URL(import.meta.url).pathname);
|
|
@@ -36692,7 +37222,7 @@ function resolveRuntimePath2() {
|
|
|
36692
37222
|
resolve26(d, "..", "..", "..", "core", "dist", "hyperframe.runtime.iife.js")
|
|
36693
37223
|
];
|
|
36694
37224
|
for (const p of candidates) {
|
|
36695
|
-
if (
|
|
37225
|
+
if (existsSync39(p)) return p;
|
|
36696
37226
|
}
|
|
36697
37227
|
return null;
|
|
36698
37228
|
}
|
|
@@ -36706,7 +37236,7 @@ function resolvePlayerPath() {
|
|
|
36706
37236
|
resolve26(d, "..", "hyperframes-player.global.js")
|
|
36707
37237
|
];
|
|
36708
37238
|
for (const p of candidates) {
|
|
36709
|
-
if (
|
|
37239
|
+
if (existsSync39(p)) return p;
|
|
36710
37240
|
}
|
|
36711
37241
|
return null;
|
|
36712
37242
|
}
|
|
@@ -36808,7 +37338,7 @@ var init_play = __esm({
|
|
|
36808
37338
|
const reqPath = ctx.req.path.replace("/composition/", "");
|
|
36809
37339
|
const filePath = resolve26(project.dir, reqPath);
|
|
36810
37340
|
if (!filePath.startsWith(project.dir)) return ctx.text("Forbidden", 403);
|
|
36811
|
-
if (!
|
|
37341
|
+
if (!existsSync39(filePath)) return ctx.text("Not found", 404);
|
|
36812
37342
|
const content = readFileSync26(filePath, "utf-8");
|
|
36813
37343
|
if (filePath.endsWith(".html")) {
|
|
36814
37344
|
const injected = injectRuntime(content);
|
|
@@ -36884,23 +37414,23 @@ var init_play = __esm({
|
|
|
36884
37414
|
});
|
|
36885
37415
|
|
|
36886
37416
|
// src/utils/publishProject.ts
|
|
36887
|
-
import { basename as basename7, join as
|
|
36888
|
-
import { readdirSync as
|
|
37417
|
+
import { basename as basename7, join as join39, relative as relative4 } from "path";
|
|
37418
|
+
import { readdirSync as readdirSync15, readFileSync as readFileSync27, statSync as statSync14 } from "fs";
|
|
36889
37419
|
import AdmZip from "adm-zip";
|
|
36890
37420
|
function shouldIgnoreSegment(segment) {
|
|
36891
37421
|
return segment.startsWith(".") || IGNORED_DIRS.has(segment) || IGNORED_FILES.has(segment);
|
|
36892
37422
|
}
|
|
36893
37423
|
function collectProjectFiles(rootDir, currentDir, paths) {
|
|
36894
|
-
for (const entry of
|
|
37424
|
+
for (const entry of readdirSync15(currentDir, { withFileTypes: true })) {
|
|
36895
37425
|
if (shouldIgnoreSegment(entry.name)) continue;
|
|
36896
|
-
const absolutePath =
|
|
37426
|
+
const absolutePath = join39(currentDir, entry.name);
|
|
36897
37427
|
const relativePath = relative4(rootDir, absolutePath).replaceAll("\\", "/");
|
|
36898
37428
|
if (!relativePath) continue;
|
|
36899
37429
|
if (entry.isDirectory()) {
|
|
36900
37430
|
collectProjectFiles(rootDir, absolutePath, paths);
|
|
36901
37431
|
continue;
|
|
36902
37432
|
}
|
|
36903
|
-
if (!
|
|
37433
|
+
if (!statSync14(absolutePath).isFile()) continue;
|
|
36904
37434
|
paths.push(relativePath);
|
|
36905
37435
|
}
|
|
36906
37436
|
}
|
|
@@ -36912,7 +37442,7 @@ function createPublishArchive(projectDir) {
|
|
|
36912
37442
|
}
|
|
36913
37443
|
const archive = new AdmZip();
|
|
36914
37444
|
for (const filePath of filePaths) {
|
|
36915
|
-
archive.addFile(filePath, readFileSync27(
|
|
37445
|
+
archive.addFile(filePath, readFileSync27(join39(projectDir, filePath)));
|
|
36916
37446
|
}
|
|
36917
37447
|
return {
|
|
36918
37448
|
buffer: archive.toBuffer(),
|
|
@@ -36968,8 +37498,8 @@ __export(publish_exports, {
|
|
|
36968
37498
|
examples: () => examples6
|
|
36969
37499
|
});
|
|
36970
37500
|
import { basename as basename8, resolve as resolve27 } from "path";
|
|
36971
|
-
import { existsSync as
|
|
36972
|
-
import { join as
|
|
37501
|
+
import { existsSync as existsSync40 } from "fs";
|
|
37502
|
+
import { join as join40 } from "path";
|
|
36973
37503
|
var examples6, publish_default;
|
|
36974
37504
|
var init_publish = __esm({
|
|
36975
37505
|
"src/commands/publish.ts"() {
|
|
@@ -37004,8 +37534,8 @@ var init_publish = __esm({
|
|
|
37004
37534
|
const dir = resolve27(rawArg ?? ".");
|
|
37005
37535
|
const isImplicitCwd = !rawArg || rawArg === "." || rawArg === "./";
|
|
37006
37536
|
const projectName = isImplicitCwd ? basename8(process.env["PWD"] ?? dir) : basename8(dir);
|
|
37007
|
-
const indexPath =
|
|
37008
|
-
if (
|
|
37537
|
+
const indexPath = join40(dir, "index.html");
|
|
37538
|
+
if (existsSync40(indexPath)) {
|
|
37009
37539
|
const lintResult = lintProject({ dir, name: projectName, indexPath });
|
|
37010
37540
|
if (lintResult.totalErrors > 0 || lintResult.totalWarnings > 0) {
|
|
37011
37541
|
console.log();
|
|
@@ -37176,9 +37706,9 @@ __export(render_exports, {
|
|
|
37176
37706
|
default: () => render_default,
|
|
37177
37707
|
examples: () => examples7
|
|
37178
37708
|
});
|
|
37179
|
-
import { mkdirSync as
|
|
37709
|
+
import { mkdirSync as mkdirSync22, readFileSync as readFileSync28, statSync as statSync15, writeFileSync as writeFileSync16, rmSync as rmSync9 } from "fs";
|
|
37180
37710
|
import { cpus as cpus3, freemem as freemem3, tmpdir as tmpdir4 } from "os";
|
|
37181
|
-
import { resolve as resolve28, dirname as dirname16, join as
|
|
37711
|
+
import { resolve as resolve28, dirname as dirname16, join as join41, basename as basename9 } from "path";
|
|
37182
37712
|
import { execFileSync as execFileSync5, spawn as spawn10 } from "child_process";
|
|
37183
37713
|
function defaultWorkerCount() {
|
|
37184
37714
|
return Math.max(1, Math.min(Math.floor(CPU_CORE_COUNT * 3 / 4), 8));
|
|
@@ -37191,7 +37721,7 @@ function resolveDockerfilePath() {
|
|
|
37191
37721
|
const devPath = resolve28(__dirname, "..", "src", "docker", "Dockerfile.render");
|
|
37192
37722
|
for (const p of [builtPath, devPath]) {
|
|
37193
37723
|
try {
|
|
37194
|
-
|
|
37724
|
+
statSync15(p);
|
|
37195
37725
|
return p;
|
|
37196
37726
|
} catch {
|
|
37197
37727
|
continue;
|
|
@@ -37215,9 +37745,9 @@ function ensureDockerImage(version, quiet) {
|
|
|
37215
37745
|
}
|
|
37216
37746
|
if (!quiet) console.log(c.dim(` Building Docker image: ${tag}...`));
|
|
37217
37747
|
const dockerfilePath = resolveDockerfilePath();
|
|
37218
|
-
const tmpDir =
|
|
37219
|
-
|
|
37220
|
-
|
|
37748
|
+
const tmpDir = join41(tmpdir4(), `hyperframes-docker-${Date.now()}`);
|
|
37749
|
+
mkdirSync22(tmpDir, { recursive: true });
|
|
37750
|
+
writeFileSync16(join41(tmpDir, "Dockerfile"), readFileSync28(dockerfilePath));
|
|
37221
37751
|
try {
|
|
37222
37752
|
execFileSync5(
|
|
37223
37753
|
"docker",
|
|
@@ -37364,6 +37894,8 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
37364
37894
|
const perf = job.perfSummary;
|
|
37365
37895
|
const compositionDurationMs = perf ? Math.round(perf.compositionDurationSeconds * 1e3) : void 0;
|
|
37366
37896
|
const speedRatio = compositionDurationMs && compositionDurationMs > 0 && elapsedMs > 0 ? Math.round(compositionDurationMs / elapsedMs * 100) / 100 : void 0;
|
|
37897
|
+
const stages = perf?.stages ?? {};
|
|
37898
|
+
const extract = perf?.videoExtractBreakdown;
|
|
37367
37899
|
trackRenderComplete({
|
|
37368
37900
|
durationMs: elapsedMs,
|
|
37369
37901
|
fps: options.fps,
|
|
@@ -37378,6 +37910,23 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
|
|
|
37378
37910
|
speedRatio,
|
|
37379
37911
|
captureAvgMs: perf?.captureAvgMs,
|
|
37380
37912
|
capturePeakMs: perf?.capturePeakMs,
|
|
37913
|
+
tmpPeakBytes: perf?.tmpPeakBytes,
|
|
37914
|
+
stageCompileMs: stages.compileMs,
|
|
37915
|
+
stageVideoExtractMs: stages.videoExtractMs,
|
|
37916
|
+
stageAudioProcessMs: stages.audioProcessMs,
|
|
37917
|
+
stageCaptureMs: stages.captureMs,
|
|
37918
|
+
stageEncodeMs: stages.encodeMs,
|
|
37919
|
+
stageAssembleMs: stages.assembleMs,
|
|
37920
|
+
extractResolveMs: extract?.resolveMs,
|
|
37921
|
+
extractHdrProbeMs: extract?.hdrProbeMs,
|
|
37922
|
+
extractHdrPreflightMs: extract?.hdrPreflightMs,
|
|
37923
|
+
extractHdrPreflightCount: extract?.hdrPreflightCount,
|
|
37924
|
+
extractVfrProbeMs: extract?.vfrProbeMs,
|
|
37925
|
+
extractVfrPreflightMs: extract?.vfrPreflightMs,
|
|
37926
|
+
extractVfrPreflightCount: extract?.vfrPreflightCount,
|
|
37927
|
+
extractPhase3Ms: extract?.extractMs,
|
|
37928
|
+
extractCacheHits: extract?.cacheHits,
|
|
37929
|
+
extractCacheMisses: extract?.cacheMisses,
|
|
37381
37930
|
...getMemorySnapshot()
|
|
37382
37931
|
});
|
|
37383
37932
|
}
|
|
@@ -37385,7 +37934,7 @@ function printRenderComplete(outputPath, elapsedMs, quiet) {
|
|
|
37385
37934
|
if (quiet) return;
|
|
37386
37935
|
let fileSize = "unknown";
|
|
37387
37936
|
try {
|
|
37388
|
-
fileSize = formatBytes(
|
|
37937
|
+
fileSize = formatBytes(statSync15(outputPath).size);
|
|
37389
37938
|
} catch {
|
|
37390
37939
|
}
|
|
37391
37940
|
const duration = formatDuration(elapsedMs);
|
|
@@ -37546,8 +38095,8 @@ var init_render2 = __esm({
|
|
|
37546
38095
|
const now = /* @__PURE__ */ new Date();
|
|
37547
38096
|
const datePart = now.toISOString().slice(0, 10);
|
|
37548
38097
|
const timePart = now.toTimeString().slice(0, 8).replace(/:/g, "-");
|
|
37549
|
-
const outputPath = args.output ? resolve28(args.output) :
|
|
37550
|
-
|
|
38098
|
+
const outputPath = args.output ? resolve28(args.output) : join41(rendersDir, `${project.name}_${datePart}_${timePart}${ext}`);
|
|
38099
|
+
mkdirSync22(dirname16(outputPath), { recursive: true });
|
|
37551
38100
|
const useDocker = args.docker ?? false;
|
|
37552
38101
|
const useGpu = args.gpu ?? false;
|
|
37553
38102
|
const quiet = args.quiet ?? false;
|
|
@@ -37894,16 +38443,16 @@ __export(info_exports, {
|
|
|
37894
38443
|
default: () => info_default,
|
|
37895
38444
|
examples: () => examples9
|
|
37896
38445
|
});
|
|
37897
|
-
import { readFileSync as readFileSync29, readdirSync as
|
|
37898
|
-
import { join as
|
|
38446
|
+
import { readFileSync as readFileSync29, readdirSync as readdirSync16, statSync as statSync16 } from "fs";
|
|
38447
|
+
import { join as join42 } from "path";
|
|
37899
38448
|
function totalSize(dir) {
|
|
37900
38449
|
let total = 0;
|
|
37901
|
-
for (const entry of
|
|
37902
|
-
const path2 =
|
|
38450
|
+
for (const entry of readdirSync16(dir, { withFileTypes: true })) {
|
|
38451
|
+
const path2 = join42(dir, entry.name);
|
|
37903
38452
|
if (entry.isDirectory()) {
|
|
37904
38453
|
total += totalSize(path2);
|
|
37905
38454
|
} else {
|
|
37906
|
-
total +=
|
|
38455
|
+
total += statSync16(path2).size;
|
|
37907
38456
|
}
|
|
37908
38457
|
}
|
|
37909
38458
|
return total;
|
|
@@ -37987,7 +38536,7 @@ __export(compositions_exports, {
|
|
|
37987
38536
|
default: () => compositions_default,
|
|
37988
38537
|
examples: () => examples10
|
|
37989
38538
|
});
|
|
37990
|
-
import { existsSync as
|
|
38539
|
+
import { existsSync as existsSync41, readFileSync as readFileSync30 } from "fs";
|
|
37991
38540
|
import { resolve as resolve29, dirname as dirname17 } from "path";
|
|
37992
38541
|
function parseCompositions(html, baseDir) {
|
|
37993
38542
|
const parser = new DOMParser();
|
|
@@ -38001,7 +38550,7 @@ function parseCompositions(html, baseDir) {
|
|
|
38001
38550
|
const compositionSrc = div.getAttribute("data-composition-src");
|
|
38002
38551
|
if (compositionSrc) {
|
|
38003
38552
|
const subPath = resolve29(baseDir, compositionSrc);
|
|
38004
|
-
if (
|
|
38553
|
+
if (existsSync41(subPath)) {
|
|
38005
38554
|
const subHtml = readFileSync30(subPath, "utf-8");
|
|
38006
38555
|
const subInfo = parseSubComposition(subHtml, id, width, height);
|
|
38007
38556
|
compositions.push({ ...subInfo, source: compositionSrc });
|
|
@@ -38134,8 +38683,8 @@ __export(benchmark_exports, {
|
|
|
38134
38683
|
default: () => benchmark_default,
|
|
38135
38684
|
examples: () => examples11
|
|
38136
38685
|
});
|
|
38137
|
-
import { existsSync as
|
|
38138
|
-
import { resolve as resolve30, join as
|
|
38686
|
+
import { existsSync as existsSync42, statSync as statSync17 } from "fs";
|
|
38687
|
+
import { resolve as resolve30, join as join43 } from "path";
|
|
38139
38688
|
var examples11, DEFAULT_CONFIGS, benchmark_default;
|
|
38140
38689
|
var init_benchmark = __esm({
|
|
38141
38690
|
"src/commands/benchmark.ts"() {
|
|
@@ -38210,7 +38759,7 @@ var init_benchmark = __esm({
|
|
|
38210
38759
|
s2?.start(`Benchmarking ${config.label}...`);
|
|
38211
38760
|
for (let i2 = 0; i2 < runsPerConfig; i2++) {
|
|
38212
38761
|
s2?.message(`${config.label} \u2014 run ${i2 + 1}/${runsPerConfig}`);
|
|
38213
|
-
const outputPath =
|
|
38762
|
+
const outputPath = join43(
|
|
38214
38763
|
benchDir,
|
|
38215
38764
|
`${config.label.replace(/[^a-zA-Z0-9]/g, "_")}_run${i2}.mp4`
|
|
38216
38765
|
);
|
|
@@ -38224,8 +38773,8 @@ var init_benchmark = __esm({
|
|
|
38224
38773
|
await producer.executeRenderJob(job, project.dir, outputPath);
|
|
38225
38774
|
const elapsedMs = Date.now() - startTime;
|
|
38226
38775
|
let fileSize = null;
|
|
38227
|
-
if (
|
|
38228
|
-
const stat3 =
|
|
38776
|
+
if (existsSync42(outputPath)) {
|
|
38777
|
+
const stat3 = statSync17(outputPath);
|
|
38229
38778
|
fileSize = stat3.size;
|
|
38230
38779
|
}
|
|
38231
38780
|
runs.push({ elapsedMs, fileSize });
|
|
@@ -38440,8 +38989,8 @@ __export(transcribe_exports2, {
|
|
|
38440
38989
|
default: () => transcribe_default,
|
|
38441
38990
|
examples: () => examples13
|
|
38442
38991
|
});
|
|
38443
|
-
import { existsSync as
|
|
38444
|
-
import { resolve as resolve31, join as
|
|
38992
|
+
import { existsSync as existsSync43, writeFileSync as writeFileSync17 } from "fs";
|
|
38993
|
+
import { resolve as resolve31, join as join44, extname as extname8 } from "path";
|
|
38445
38994
|
async function importTranscript(inputPath, dir, json) {
|
|
38446
38995
|
const { loadTranscript: loadTranscript2, patchCaptionHtml: patchCaptionHtml2 } = await Promise.resolve().then(() => (init_normalize(), normalize_exports));
|
|
38447
38996
|
const { words, format } = loadTranscript2(inputPath);
|
|
@@ -38449,8 +38998,8 @@ async function importTranscript(inputPath, dir, json) {
|
|
|
38449
38998
|
console.error(c.error("No words found in transcript."));
|
|
38450
38999
|
process.exit(1);
|
|
38451
39000
|
}
|
|
38452
|
-
const outPath =
|
|
38453
|
-
|
|
39001
|
+
const outPath = join44(dir, "transcript.json");
|
|
39002
|
+
writeFileSync17(outPath, JSON.stringify(words, null, 2));
|
|
38454
39003
|
patchCaptionHtml2(dir, words);
|
|
38455
39004
|
if (json) {
|
|
38456
39005
|
console.log(
|
|
@@ -38485,7 +39034,7 @@ async function transcribeAudio(inputPath, dir, opts) {
|
|
|
38485
39034
|
);
|
|
38486
39035
|
}
|
|
38487
39036
|
}
|
|
38488
|
-
|
|
39037
|
+
writeFileSync17(result.transcriptPath, JSON.stringify(words, null, 2));
|
|
38489
39038
|
patchCaptionHtml2(dir, words);
|
|
38490
39039
|
if (opts.json) {
|
|
38491
39040
|
console.log(
|
|
@@ -38566,7 +39115,7 @@ var init_transcribe2 = __esm({
|
|
|
38566
39115
|
},
|
|
38567
39116
|
async run({ args }) {
|
|
38568
39117
|
const inputPath = resolve31(args.input);
|
|
38569
|
-
if (!
|
|
39118
|
+
if (!existsSync43(inputPath)) {
|
|
38570
39119
|
console.error(c.error(`File not found: ${args.input}`));
|
|
38571
39120
|
process.exit(1);
|
|
38572
39121
|
}
|
|
@@ -38587,9 +39136,9 @@ var init_transcribe2 = __esm({
|
|
|
38587
39136
|
});
|
|
38588
39137
|
|
|
38589
39138
|
// src/tts/manager.ts
|
|
38590
|
-
import { existsSync as
|
|
39139
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync23 } from "fs";
|
|
38591
39140
|
import { homedir as homedir8 } from "os";
|
|
38592
|
-
import { join as
|
|
39141
|
+
import { join as join45 } from "path";
|
|
38593
39142
|
function inferLangFromVoiceId(voiceId) {
|
|
38594
39143
|
const first = voiceId.charAt(0).toLowerCase();
|
|
38595
39144
|
return VOICE_PREFIX_LANG[first] ?? "en-us";
|
|
@@ -38598,29 +39147,29 @@ function isSupportedLang(value) {
|
|
|
38598
39147
|
return SUPPORTED_LANGS.includes(value);
|
|
38599
39148
|
}
|
|
38600
39149
|
async function ensureModel2(model = DEFAULT_MODEL2, options) {
|
|
38601
|
-
const modelPath =
|
|
38602
|
-
if (
|
|
39150
|
+
const modelPath = join45(MODELS_DIR2, `${model}.onnx`);
|
|
39151
|
+
if (existsSync44(modelPath)) return modelPath;
|
|
38603
39152
|
const url = MODEL_URLS[model];
|
|
38604
39153
|
if (!url) {
|
|
38605
39154
|
throw new Error(
|
|
38606
39155
|
`Unknown TTS model: ${model}. Available: ${Object.keys(MODEL_URLS).join(", ")}`
|
|
38607
39156
|
);
|
|
38608
39157
|
}
|
|
38609
|
-
|
|
39158
|
+
mkdirSync23(MODELS_DIR2, { recursive: true });
|
|
38610
39159
|
options?.onProgress?.(`Downloading TTS model ${model} (~311 MB)...`);
|
|
38611
39160
|
await downloadFile(url, modelPath);
|
|
38612
|
-
if (!
|
|
39161
|
+
if (!existsSync44(modelPath)) {
|
|
38613
39162
|
throw new Error(`Model download failed: ${model}`);
|
|
38614
39163
|
}
|
|
38615
39164
|
return modelPath;
|
|
38616
39165
|
}
|
|
38617
39166
|
async function ensureVoices(options) {
|
|
38618
|
-
const voicesPath =
|
|
38619
|
-
if (
|
|
38620
|
-
|
|
39167
|
+
const voicesPath = join45(VOICES_DIR, "voices-v1.0.bin");
|
|
39168
|
+
if (existsSync44(voicesPath)) return voicesPath;
|
|
39169
|
+
mkdirSync23(VOICES_DIR, { recursive: true });
|
|
38621
39170
|
options?.onProgress?.("Downloading voice data (~27 MB)...");
|
|
38622
39171
|
await downloadFile(VOICES_URL, voicesPath);
|
|
38623
|
-
if (!
|
|
39172
|
+
if (!existsSync44(voicesPath)) {
|
|
38624
39173
|
throw new Error("Voice data download failed");
|
|
38625
39174
|
}
|
|
38626
39175
|
return voicesPath;
|
|
@@ -38630,9 +39179,9 @@ var init_manager3 = __esm({
|
|
|
38630
39179
|
"src/tts/manager.ts"() {
|
|
38631
39180
|
"use strict";
|
|
38632
39181
|
init_download();
|
|
38633
|
-
CACHE_DIR3 =
|
|
38634
|
-
MODELS_DIR2 =
|
|
38635
|
-
VOICES_DIR =
|
|
39182
|
+
CACHE_DIR3 = join45(homedir8(), ".cache", "hyperframes", "tts");
|
|
39183
|
+
MODELS_DIR2 = join45(CACHE_DIR3, "models");
|
|
39184
|
+
VOICES_DIR = join45(CACHE_DIR3, "voices");
|
|
38636
39185
|
DEFAULT_MODEL2 = "kokoro-v1.0";
|
|
38637
39186
|
MODEL_URLS = {
|
|
38638
39187
|
"kokoro-v1.0": "https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/kokoro-v1.0.onnx"
|
|
@@ -38693,8 +39242,8 @@ __export(synthesize_exports, {
|
|
|
38693
39242
|
synthesize: () => synthesize
|
|
38694
39243
|
});
|
|
38695
39244
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
38696
|
-
import { existsSync as
|
|
38697
|
-
import { join as
|
|
39245
|
+
import { existsSync as existsSync45, writeFileSync as writeFileSync18, mkdirSync as mkdirSync24, readdirSync as readdirSync17, unlinkSync as unlinkSync6 } from "fs";
|
|
39246
|
+
import { join as join46, dirname as dirname18, basename as basename10 } from "path";
|
|
38698
39247
|
import { homedir as homedir9 } from "os";
|
|
38699
39248
|
function findPython() {
|
|
38700
39249
|
for (const name of ["python3", "python"]) {
|
|
@@ -38730,15 +39279,15 @@ function hasPythonPackage(python, pkg) {
|
|
|
38730
39279
|
}
|
|
38731
39280
|
}
|
|
38732
39281
|
function ensureSynthScript() {
|
|
38733
|
-
if (!
|
|
38734
|
-
|
|
38735
|
-
|
|
39282
|
+
if (!existsSync45(SCRIPT_PATH)) {
|
|
39283
|
+
mkdirSync24(SCRIPT_DIR, { recursive: true });
|
|
39284
|
+
writeFileSync18(SCRIPT_PATH, SYNTH_SCRIPT);
|
|
38736
39285
|
const currentName = basename10(SCRIPT_PATH);
|
|
38737
39286
|
try {
|
|
38738
|
-
for (const entry of
|
|
39287
|
+
for (const entry of readdirSync17(SCRIPT_DIR)) {
|
|
38739
39288
|
if (entry !== currentName && /^synth(-v\d+)?\.py$/.test(entry)) {
|
|
38740
39289
|
try {
|
|
38741
|
-
unlinkSync6(
|
|
39290
|
+
unlinkSync6(join46(SCRIPT_DIR, entry));
|
|
38742
39291
|
} catch {
|
|
38743
39292
|
}
|
|
38744
39293
|
}
|
|
@@ -38772,7 +39321,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
38772
39321
|
ensureVoices({ onProgress: options?.onProgress })
|
|
38773
39322
|
]);
|
|
38774
39323
|
const scriptPath = ensureSynthScript();
|
|
38775
|
-
|
|
39324
|
+
mkdirSync24(dirname18(outputPath), { recursive: true });
|
|
38776
39325
|
options?.onProgress?.(`Generating speech with voice ${voice} (${lang})...`);
|
|
38777
39326
|
try {
|
|
38778
39327
|
const stdout2 = execFileSync6(
|
|
@@ -38784,7 +39333,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
38784
39333
|
stdio: ["pipe", "pipe", "pipe"]
|
|
38785
39334
|
}
|
|
38786
39335
|
);
|
|
38787
|
-
if (!
|
|
39336
|
+
if (!existsSync45(outputPath)) {
|
|
38788
39337
|
throw new Error("Synthesis completed but no output file was created");
|
|
38789
39338
|
}
|
|
38790
39339
|
const lines = stdout2.trim().split("\n");
|
|
@@ -38797,7 +39346,7 @@ async function synthesize(text, outputPath, options) {
|
|
|
38797
39346
|
langApplied: result.langApplied
|
|
38798
39347
|
};
|
|
38799
39348
|
} catch (err) {
|
|
38800
|
-
if (err instanceof SyntaxError &&
|
|
39349
|
+
if (err instanceof SyntaxError && existsSync45(outputPath)) {
|
|
38801
39350
|
throw new Error(
|
|
38802
39351
|
"Speech was generated but metadata could not be read. Check the output file manually."
|
|
38803
39352
|
);
|
|
@@ -38848,8 +39397,8 @@ print(json.dumps({
|
|
|
38848
39397
|
"langApplied": bool(lang and supports_lang),
|
|
38849
39398
|
}))
|
|
38850
39399
|
`;
|
|
38851
|
-
SCRIPT_DIR =
|
|
38852
|
-
SCRIPT_PATH =
|
|
39400
|
+
SCRIPT_DIR = join46(homedir9(), ".cache", "hyperframes", "tts");
|
|
39401
|
+
SCRIPT_PATH = join46(SCRIPT_DIR, "synth-v2.py");
|
|
38853
39402
|
}
|
|
38854
39403
|
});
|
|
38855
39404
|
|
|
@@ -38859,7 +39408,7 @@ __export(tts_exports, {
|
|
|
38859
39408
|
default: () => tts_default,
|
|
38860
39409
|
examples: () => examples14
|
|
38861
39410
|
});
|
|
38862
|
-
import { existsSync as
|
|
39411
|
+
import { existsSync as existsSync46, readFileSync as readFileSync31 } from "fs";
|
|
38863
39412
|
import { resolve as resolve32, extname as extname9 } from "path";
|
|
38864
39413
|
function listVoices(json) {
|
|
38865
39414
|
const rows = BUNDLED_VOICES.map((v) => ({ ...v, defaultLang: inferLangFromVoiceId(v.id) }));
|
|
@@ -38969,7 +39518,7 @@ var init_tts = __esm({
|
|
|
38969
39518
|
}
|
|
38970
39519
|
let text;
|
|
38971
39520
|
const maybeFile = resolve32(args.input);
|
|
38972
|
-
if (
|
|
39521
|
+
if (existsSync46(maybeFile) && extname9(maybeFile).toLowerCase() === ".txt") {
|
|
38973
39522
|
text = readFileSync31(maybeFile, "utf-8").trim();
|
|
38974
39523
|
if (!text) {
|
|
38975
39524
|
console.error(c.error("File is empty."));
|
|
@@ -39062,15 +39611,15 @@ __export(docs_exports, {
|
|
|
39062
39611
|
default: () => docs_default,
|
|
39063
39612
|
examples: () => examples15
|
|
39064
39613
|
});
|
|
39065
|
-
import { readFileSync as readFileSync32, existsSync as
|
|
39066
|
-
import { resolve as resolve33, dirname as dirname19, join as
|
|
39614
|
+
import { readFileSync as readFileSync32, existsSync as existsSync47 } from "fs";
|
|
39615
|
+
import { resolve as resolve33, dirname as dirname19, join as join47 } from "path";
|
|
39067
39616
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
39068
39617
|
function docsDir() {
|
|
39069
39618
|
const thisFile = fileURLToPath6(import.meta.url);
|
|
39070
39619
|
const dir = dirname19(thisFile);
|
|
39071
39620
|
const devPath = resolve33(dir, "..", "docs");
|
|
39072
39621
|
const builtPath = resolve33(dir, "docs");
|
|
39073
|
-
return
|
|
39622
|
+
return existsSync47(devPath) ? devPath : builtPath;
|
|
39074
39623
|
}
|
|
39075
39624
|
function formatInlineCode(line) {
|
|
39076
39625
|
return line.replace(/`([^`]+)`/g, (_match, code) => c.accent(code));
|
|
@@ -39167,8 +39716,8 @@ var init_docs = __esm({
|
|
|
39167
39716
|
}
|
|
39168
39717
|
process.exit(1);
|
|
39169
39718
|
}
|
|
39170
|
-
const filePath =
|
|
39171
|
-
if (!
|
|
39719
|
+
const filePath = join47(docsDir(), entry.file);
|
|
39720
|
+
if (!existsSync47(filePath)) {
|
|
39172
39721
|
console.error(c.error(`Doc file not found: ${filePath}`));
|
|
39173
39722
|
process.exit(1);
|
|
39174
39723
|
}
|
|
@@ -39598,8 +40147,8 @@ var validate_exports = {};
|
|
|
39598
40147
|
__export(validate_exports, {
|
|
39599
40148
|
default: () => validate_default
|
|
39600
40149
|
});
|
|
39601
|
-
import { existsSync as
|
|
39602
|
-
import { resolve as resolve34, join as
|
|
40150
|
+
import { existsSync as existsSync48, readFileSync as readFileSync33 } from "fs";
|
|
40151
|
+
import { resolve as resolve34, join as join48, dirname as dirname20 } from "path";
|
|
39603
40152
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
39604
40153
|
async function getCompositionDuration2(page) {
|
|
39605
40154
|
return page.evaluate(() => {
|
|
@@ -39654,7 +40203,7 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
39654
40203
|
"dist",
|
|
39655
40204
|
"hyperframe.runtime.iife.js"
|
|
39656
40205
|
);
|
|
39657
|
-
if (
|
|
40206
|
+
if (existsSync48(runtimePath)) {
|
|
39658
40207
|
const runtimeSource = readFileSync33(runtimePath, "utf-8");
|
|
39659
40208
|
html = html.replace(
|
|
39660
40209
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
@@ -39670,8 +40219,8 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
39670
40219
|
res.end(html);
|
|
39671
40220
|
return;
|
|
39672
40221
|
}
|
|
39673
|
-
const filePath =
|
|
39674
|
-
if (
|
|
40222
|
+
const filePath = join48(projectDir, decodeURIComponent(url));
|
|
40223
|
+
if (existsSync48(filePath)) {
|
|
39675
40224
|
res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
|
|
39676
40225
|
res.end(readFileSync33(filePath));
|
|
39677
40226
|
return;
|
|
@@ -39864,13 +40413,13 @@ __export(snapshot_exports, {
|
|
|
39864
40413
|
examples: () => examples19
|
|
39865
40414
|
});
|
|
39866
40415
|
import { spawn as spawn11 } from "child_process";
|
|
39867
|
-
import { existsSync as
|
|
40416
|
+
import { existsSync as existsSync49, mkdtempSync as mkdtempSync3, readFileSync as readFileSync34, mkdirSync as mkdirSync25, rmSync as rmSync10 } from "fs";
|
|
39868
40417
|
import { tmpdir as tmpdir5 } from "os";
|
|
39869
|
-
import { resolve as resolve35, join as
|
|
40418
|
+
import { resolve as resolve35, join as join49, dirname as dirname21, relative as relative5, isAbsolute as isAbsolute6 } from "path";
|
|
39870
40419
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
39871
40420
|
async function extractVideoFrameToBuffer(videoPath, timeSeconds) {
|
|
39872
|
-
const tmp = mkdtempSync3(
|
|
39873
|
-
const outPath =
|
|
40421
|
+
const tmp = mkdtempSync3(join49(tmpdir5(), "hf-snapshot-frame-"));
|
|
40422
|
+
const outPath = join49(tmp, "frame.png");
|
|
39874
40423
|
try {
|
|
39875
40424
|
const result = await new Promise(
|
|
39876
40425
|
(resolvePromise) => {
|
|
@@ -39908,7 +40457,7 @@ async function extractVideoFrameToBuffer(videoPath, timeSeconds) {
|
|
|
39908
40457
|
});
|
|
39909
40458
|
}
|
|
39910
40459
|
);
|
|
39911
|
-
if (result.code !== 0 || result.timedOut || !
|
|
40460
|
+
if (result.code !== 0 || result.timedOut || !existsSync49(outPath)) return null;
|
|
39912
40461
|
return readFileSync34(outPath);
|
|
39913
40462
|
} finally {
|
|
39914
40463
|
try {
|
|
@@ -39931,7 +40480,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
39931
40480
|
"dist",
|
|
39932
40481
|
"hyperframe.runtime.iife.js"
|
|
39933
40482
|
);
|
|
39934
|
-
if (
|
|
40483
|
+
if (existsSync49(runtimePath)) {
|
|
39935
40484
|
const runtimeSource = readFileSync34(runtimePath, "utf-8");
|
|
39936
40485
|
html = html.replace(
|
|
39937
40486
|
/<script[^>]*data-hyperframes-preview-runtime[^>]*src="[^"]*"[^>]*><\/script>/,
|
|
@@ -39954,7 +40503,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
39954
40503
|
res.end();
|
|
39955
40504
|
return;
|
|
39956
40505
|
}
|
|
39957
|
-
if (
|
|
40506
|
+
if (existsSync49(filePath)) {
|
|
39958
40507
|
res.writeHead(200, { "Content-Type": getMimeType2(filePath) });
|
|
39959
40508
|
res.end(readFileSync34(filePath));
|
|
39960
40509
|
return;
|
|
@@ -40029,8 +40578,8 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
40029
40578
|
return [];
|
|
40030
40579
|
}
|
|
40031
40580
|
const positions = opts.at?.length ? opts.at : numFrames === 1 ? [duration / 2] : Array.from({ length: numFrames }, (_2, i2) => i2 / (numFrames - 1) * duration);
|
|
40032
|
-
const snapshotDir =
|
|
40033
|
-
|
|
40581
|
+
const snapshotDir = join49(projectDir, "snapshots");
|
|
40582
|
+
mkdirSync25(snapshotDir, { recursive: true });
|
|
40034
40583
|
let injectVideoFramesBatch2 = null;
|
|
40035
40584
|
let syncVideoFrameVisibility2 = null;
|
|
40036
40585
|
try {
|
|
@@ -40090,7 +40639,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
40090
40639
|
const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, "");
|
|
40091
40640
|
const candidate = resolve35(projectDir, decodedPath);
|
|
40092
40641
|
const rel = relative5(projectDir, candidate);
|
|
40093
|
-
if (!rel.startsWith("..") && !isAbsolute6(rel) &&
|
|
40642
|
+
if (!rel.startsWith("..") && !isAbsolute6(rel) && existsSync49(candidate)) {
|
|
40094
40643
|
filePath = candidate;
|
|
40095
40644
|
}
|
|
40096
40645
|
} catch {
|
|
@@ -40116,7 +40665,7 @@ async function captureSnapshots(projectDir, opts) {
|
|
|
40116
40665
|
}
|
|
40117
40666
|
const timeLabel = opts.at?.length ? `${time.toFixed(1)}s` : `${Math.round(time / duration * 100)}pct`;
|
|
40118
40667
|
const filename = `frame-${String(i2).padStart(2, "0")}-at-${timeLabel}.png`;
|
|
40119
|
-
const framePath =
|
|
40668
|
+
const framePath = join49(snapshotDir, filename);
|
|
40120
40669
|
await page.screenshot({ path: framePath, type: "png" });
|
|
40121
40670
|
savedPaths.push(`snapshots/${filename}`);
|
|
40122
40671
|
}
|
|
@@ -40201,14 +40750,14 @@ ${c.error("\u2717")} Snapshot failed: ${msg}`);
|
|
|
40201
40750
|
});
|
|
40202
40751
|
|
|
40203
40752
|
// src/capture/assetDownloader.ts
|
|
40204
|
-
import { writeFileSync as
|
|
40205
|
-
import { join as
|
|
40753
|
+
import { writeFileSync as writeFileSync19, mkdirSync as mkdirSync26 } from "fs";
|
|
40754
|
+
import { join as join50, extname as extname10 } from "path";
|
|
40206
40755
|
async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks) {
|
|
40207
|
-
const assetsDir =
|
|
40208
|
-
|
|
40756
|
+
const assetsDir = join50(outputDir, "assets");
|
|
40757
|
+
mkdirSync26(assetsDir, { recursive: true });
|
|
40209
40758
|
const assets = [];
|
|
40210
40759
|
const downloadedUrls = /* @__PURE__ */ new Set();
|
|
40211
|
-
|
|
40760
|
+
mkdirSync26(join50(outputDir, "assets", "svgs"), { recursive: true });
|
|
40212
40761
|
for (let i2 = 0; i2 < tokens.svgs.length && i2 < 30; i2++) {
|
|
40213
40762
|
const svg = tokens.svgs[i2];
|
|
40214
40763
|
if (!svg.outerHTML || svg.outerHTML.length < 50) continue;
|
|
@@ -40216,7 +40765,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
40216
40765
|
const name = label2 ? slugify(label2) + ".svg" : svg.isLogo ? `logo-${i2}.svg` : `icon-${i2}.svg`;
|
|
40217
40766
|
const localPath = `assets/svgs/${name}`;
|
|
40218
40767
|
try {
|
|
40219
|
-
|
|
40768
|
+
writeFileSync19(join50(outputDir, localPath), svg.outerHTML, "utf-8");
|
|
40220
40769
|
assets.push({ url: "", localPath, type: "svg" });
|
|
40221
40770
|
} catch {
|
|
40222
40771
|
}
|
|
@@ -40229,7 +40778,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
40229
40778
|
const localPath = `assets/${name}`;
|
|
40230
40779
|
const buffer = await fetchBuffer(icon.href);
|
|
40231
40780
|
if (buffer) {
|
|
40232
|
-
|
|
40781
|
+
writeFileSync19(join50(outputDir, localPath), buffer);
|
|
40233
40782
|
assets.push({ url: icon.href, localPath, type: "favicon" });
|
|
40234
40783
|
break;
|
|
40235
40784
|
}
|
|
@@ -40286,7 +40835,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
40286
40835
|
const slug = isMeaningful ? slugify(rawName) : `${prefix}-${imgIdx}`;
|
|
40287
40836
|
const name = `${slug}${ext}`;
|
|
40288
40837
|
const localPath = `assets/${name}`;
|
|
40289
|
-
|
|
40838
|
+
writeFileSync19(join50(outputDir, localPath), buffer);
|
|
40290
40839
|
assets.push({ url, localPath, type: "image" });
|
|
40291
40840
|
imgIdx++;
|
|
40292
40841
|
} catch {
|
|
@@ -40299,7 +40848,7 @@ async function downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks)
|
|
|
40299
40848
|
const localPath = `assets/og-image${ext}`;
|
|
40300
40849
|
const buffer = await fetchBuffer(tokens.ogImage);
|
|
40301
40850
|
if (buffer && buffer.length > 5e3) {
|
|
40302
|
-
|
|
40851
|
+
writeFileSync19(join50(outputDir, localPath), buffer);
|
|
40303
40852
|
assets.push({ url: tokens.ogImage, localPath, type: "image" });
|
|
40304
40853
|
}
|
|
40305
40854
|
} catch {
|
|
@@ -40322,8 +40871,8 @@ function normalizeUrl(u) {
|
|
|
40322
40871
|
}
|
|
40323
40872
|
}
|
|
40324
40873
|
async function downloadAndRewriteFonts(css, outputDir) {
|
|
40325
|
-
const assetsDir =
|
|
40326
|
-
|
|
40874
|
+
const assetsDir = join50(outputDir, "assets", "fonts");
|
|
40875
|
+
mkdirSync26(assetsDir, { recursive: true });
|
|
40327
40876
|
const fontUrlRegex = /url\(['"]?(https?:\/\/[^'")\s]+\.(?:woff2?|ttf|otf)[^'")\s]*?)['"]?\)/g;
|
|
40328
40877
|
const fontUrls = /* @__PURE__ */ new Set();
|
|
40329
40878
|
let match;
|
|
@@ -40358,11 +40907,11 @@ async function downloadAndRewriteFonts(css, outputDir) {
|
|
|
40358
40907
|
try {
|
|
40359
40908
|
const urlObj = new URL(fontUrl);
|
|
40360
40909
|
const filename = urlObj.pathname.split("/").pop() || `font-${count}.woff2`;
|
|
40361
|
-
const localPath =
|
|
40910
|
+
const localPath = join50(assetsDir, filename);
|
|
40362
40911
|
const relativePath = `assets/fonts/${filename}`;
|
|
40363
40912
|
const buffer = await fetchBuffer(fontUrl);
|
|
40364
40913
|
if (buffer) {
|
|
40365
|
-
|
|
40914
|
+
writeFileSync19(localPath, buffer);
|
|
40366
40915
|
rewritten = rewritten.split(fontUrl).join(relativePath);
|
|
40367
40916
|
familyCounts.set(family, familyCount + 1);
|
|
40368
40917
|
count++;
|
|
@@ -41155,8 +41704,8 @@ var init_animationCataloger = __esm({
|
|
|
41155
41704
|
});
|
|
41156
41705
|
|
|
41157
41706
|
// src/capture/mediaCapture.ts
|
|
41158
|
-
import { mkdirSync as
|
|
41159
|
-
import { join as
|
|
41707
|
+
import { mkdirSync as mkdirSync27, writeFileSync as writeFileSync20, readdirSync as readdirSync18, readFileSync as readFileSync35, statSync as statSync18 } from "fs";
|
|
41708
|
+
import { join as join51 } from "path";
|
|
41160
41709
|
async function saveLottieAnimations(discoveredLotties, lottieDir) {
|
|
41161
41710
|
let savedCount = 0;
|
|
41162
41711
|
const savedHashes = /* @__PURE__ */ new Set();
|
|
@@ -41189,7 +41738,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
|
|
|
41189
41738
|
const hash2 = buf.toString("base64").slice(0, 100);
|
|
41190
41739
|
if (savedHashes.has(hash2)) continue;
|
|
41191
41740
|
savedHashes.add(hash2);
|
|
41192
|
-
|
|
41741
|
+
writeFileSync20(join51(lottieDir, `animation-${savedCount}.lottie`), buf);
|
|
41193
41742
|
savedCount++;
|
|
41194
41743
|
continue;
|
|
41195
41744
|
}
|
|
@@ -41207,7 +41756,7 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
|
|
|
41207
41756
|
} catch {
|
|
41208
41757
|
continue;
|
|
41209
41758
|
}
|
|
41210
|
-
|
|
41759
|
+
writeFileSync20(join51(lottieDir, `animation-${savedCount}.json`), jsonData, "utf-8");
|
|
41211
41760
|
savedCount++;
|
|
41212
41761
|
}
|
|
41213
41762
|
} catch {
|
|
@@ -41217,22 +41766,22 @@ async function saveLottieAnimations(discoveredLotties, lottieDir) {
|
|
|
41217
41766
|
}
|
|
41218
41767
|
async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
|
|
41219
41768
|
const manifest = [];
|
|
41220
|
-
const previewDir =
|
|
41221
|
-
|
|
41222
|
-
for (const file of
|
|
41769
|
+
const previewDir = join51(lottieDir, "previews");
|
|
41770
|
+
mkdirSync27(previewDir, { recursive: true });
|
|
41771
|
+
for (const file of readdirSync18(lottieDir)) {
|
|
41223
41772
|
if (!file.endsWith(".json")) continue;
|
|
41224
41773
|
try {
|
|
41225
|
-
const raw = JSON.parse(readFileSync35(
|
|
41774
|
+
const raw = JSON.parse(readFileSync35(join51(lottieDir, file), "utf-8"));
|
|
41226
41775
|
const fr = raw.fr || 30;
|
|
41227
41776
|
const dur = ((raw.op || 0) - (raw.ip || 0)) / fr;
|
|
41228
41777
|
const previewName = file.replace(".json", "-preview.png");
|
|
41229
|
-
const fileSize =
|
|
41778
|
+
const fileSize = statSync18(join51(lottieDir, file)).size;
|
|
41230
41779
|
if (fileSize > 2e6) continue;
|
|
41231
41780
|
let previewPage;
|
|
41232
41781
|
try {
|
|
41233
41782
|
previewPage = await chromeBrowser.newPage();
|
|
41234
41783
|
await previewPage.setViewport({ width: 400, height: 400 });
|
|
41235
|
-
const animData = JSON.parse(readFileSync35(
|
|
41784
|
+
const animData = JSON.parse(readFileSync35(join51(lottieDir, file), "utf-8"));
|
|
41236
41785
|
const midFrame = Math.floor(((raw.op || 0) - (raw.ip || 0)) * 0.3);
|
|
41237
41786
|
await previewPage.setContent(
|
|
41238
41787
|
`<!DOCTYPE html>
|
|
@@ -41262,7 +41811,7 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
|
|
|
41262
41811
|
await previewPage.waitForFunction(() => window.__READY === true, { timeout: 5e3 }).catch(() => {
|
|
41263
41812
|
});
|
|
41264
41813
|
await previewPage.screenshot({
|
|
41265
|
-
path:
|
|
41814
|
+
path: join51(previewDir, previewName),
|
|
41266
41815
|
type: "png",
|
|
41267
41816
|
omitBackground: true
|
|
41268
41817
|
});
|
|
@@ -41285,8 +41834,8 @@ async function renderLottiePreviews(chromeBrowser, lottieDir, outputDir) {
|
|
|
41285
41834
|
}
|
|
41286
41835
|
}
|
|
41287
41836
|
if (manifest.length > 0) {
|
|
41288
|
-
|
|
41289
|
-
|
|
41837
|
+
writeFileSync20(
|
|
41838
|
+
join51(outputDir, "extracted", "lottie-manifest.json"),
|
|
41290
41839
|
JSON.stringify(manifest, null, 2),
|
|
41291
41840
|
"utf-8"
|
|
41292
41841
|
);
|
|
@@ -41348,15 +41897,15 @@ async function captureVideoManifest(page, outputDir, progress) {
|
|
|
41348
41897
|
return true;
|
|
41349
41898
|
});
|
|
41350
41899
|
if (uniqueVideos.length > 0) {
|
|
41351
|
-
const videoManifestDir =
|
|
41352
|
-
|
|
41353
|
-
const previewDir =
|
|
41354
|
-
|
|
41900
|
+
const videoManifestDir = join51(outputDir, "assets", "videos");
|
|
41901
|
+
mkdirSync27(videoManifestDir, { recursive: true });
|
|
41902
|
+
const previewDir = join51(videoManifestDir, "previews");
|
|
41903
|
+
mkdirSync27(previewDir, { recursive: true });
|
|
41355
41904
|
const videoManifest = [];
|
|
41356
41905
|
for (let vi = 0; vi < uniqueVideos.length && vi < 20; vi++) {
|
|
41357
41906
|
const v = uniqueVideos[vi];
|
|
41358
41907
|
const previewName = `video-${vi}-preview.png`;
|
|
41359
|
-
const previewPath =
|
|
41908
|
+
const previewPath = join51(previewDir, previewName);
|
|
41360
41909
|
try {
|
|
41361
41910
|
await page.evaluate(`window.scrollTo(0, ${Math.max(0, v.top - 100)})`);
|
|
41362
41911
|
await new Promise((r2) => setTimeout(r2, 300));
|
|
@@ -41394,8 +41943,8 @@ async function captureVideoManifest(page, outputDir, progress) {
|
|
|
41394
41943
|
});
|
|
41395
41944
|
}
|
|
41396
41945
|
if (videoManifest.length > 0) {
|
|
41397
|
-
|
|
41398
|
-
|
|
41946
|
+
writeFileSync20(
|
|
41947
|
+
join51(outputDir, "extracted", "video-manifest.json"),
|
|
41399
41948
|
JSON.stringify(videoManifest, null, 2),
|
|
41400
41949
|
"utf-8"
|
|
41401
41950
|
);
|
|
@@ -48349,7 +48898,7 @@ var require_node_domexception = __commonJS({
|
|
|
48349
48898
|
});
|
|
48350
48899
|
|
|
48351
48900
|
// ../../node_modules/.bun/fetch-blob@3.2.0/node_modules/fetch-blob/from.js
|
|
48352
|
-
import { statSync as
|
|
48901
|
+
import { statSync as statSync19, createReadStream as createReadStream2, promises as fs2 } from "fs";
|
|
48353
48902
|
import { basename as basename11 } from "path";
|
|
48354
48903
|
var import_node_domexception, stat, blobFromSync, blobFrom, fileFrom, fileFromSync, fromBlob, fromFile, BlobDataItem;
|
|
48355
48904
|
var init_from = __esm({
|
|
@@ -48359,10 +48908,10 @@ var init_from = __esm({
|
|
|
48359
48908
|
init_file();
|
|
48360
48909
|
init_fetch_blob();
|
|
48361
48910
|
({ stat } = fs2);
|
|
48362
|
-
blobFromSync = (path2, type) => fromBlob(
|
|
48911
|
+
blobFromSync = (path2, type) => fromBlob(statSync19(path2), path2, type);
|
|
48363
48912
|
blobFrom = (path2, type) => stat(path2).then((stat3) => fromBlob(stat3, path2, type));
|
|
48364
48913
|
fileFrom = (path2, type) => stat(path2).then((stat3) => fromFile(stat3, path2, type));
|
|
48365
|
-
fileFromSync = (path2, type) => fromFile(
|
|
48914
|
+
fileFromSync = (path2, type) => fromFile(statSync19(path2), path2, type);
|
|
48366
48915
|
fromBlob = (stat3, path2, type = "") => new fetch_blob_default([new BlobDataItem({
|
|
48367
48916
|
path: path2,
|
|
48368
48917
|
size: stat3.size,
|
|
@@ -62153,7 +62702,7 @@ var require_websocket = __commonJS({
|
|
|
62153
62702
|
var http4 = __require("http");
|
|
62154
62703
|
var net2 = __require("net");
|
|
62155
62704
|
var tls = __require("tls");
|
|
62156
|
-
var { randomBytes, createHash:
|
|
62705
|
+
var { randomBytes, createHash: createHash4 } = __require("crypto");
|
|
62157
62706
|
var { Duplex, Readable: Readable3 } = __require("stream");
|
|
62158
62707
|
var { URL: URL2 } = __require("url");
|
|
62159
62708
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -62813,7 +63362,7 @@ var require_websocket = __commonJS({
|
|
|
62813
63362
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
62814
63363
|
return;
|
|
62815
63364
|
}
|
|
62816
|
-
const digest =
|
|
63365
|
+
const digest = createHash4("sha1").update(key2 + GUID).digest("base64");
|
|
62817
63366
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
62818
63367
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
62819
63368
|
return;
|
|
@@ -63180,7 +63729,7 @@ var require_websocket_server = __commonJS({
|
|
|
63180
63729
|
var EventEmitter = __require("events");
|
|
63181
63730
|
var http4 = __require("http");
|
|
63182
63731
|
var { Duplex } = __require("stream");
|
|
63183
|
-
var { createHash:
|
|
63732
|
+
var { createHash: createHash4 } = __require("crypto");
|
|
63184
63733
|
var extension = require_extension();
|
|
63185
63734
|
var PerMessageDeflate = require_permessage_deflate();
|
|
63186
63735
|
var subprotocol = require_subprotocol();
|
|
@@ -63481,7 +64030,7 @@ var require_websocket_server = __commonJS({
|
|
|
63481
64030
|
);
|
|
63482
64031
|
}
|
|
63483
64032
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
63484
|
-
const digest =
|
|
64033
|
+
const digest = createHash4("sha1").update(key2 + GUID).digest("base64");
|
|
63485
64034
|
const headers = [
|
|
63486
64035
|
"HTTP/1.1 101 Switching Protocols",
|
|
63487
64036
|
"Upgrade: websocket",
|
|
@@ -81850,8 +82399,8 @@ ${underline2}`);
|
|
|
81850
82399
|
});
|
|
81851
82400
|
|
|
81852
82401
|
// src/capture/contentExtractor.ts
|
|
81853
|
-
import { readdirSync as
|
|
81854
|
-
import { join as
|
|
82402
|
+
import { readdirSync as readdirSync19, statSync as statSync20, readFileSync as readFileSync36 } from "fs";
|
|
82403
|
+
import { join as join52 } from "path";
|
|
81855
82404
|
async function detectLibraries(page, capturedShaders) {
|
|
81856
82405
|
let detectedLibraries = [];
|
|
81857
82406
|
try {
|
|
@@ -81971,7 +82520,7 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
|
|
|
81971
82520
|
try {
|
|
81972
82521
|
const { GoogleGenAI: GoogleGenAI2 } = await Promise.resolve().then(() => (init_node4(), node_exports));
|
|
81973
82522
|
const ai = new GoogleGenAI2({ apiKey: geminiKey });
|
|
81974
|
-
const imageFiles =
|
|
82523
|
+
const imageFiles = readdirSync19(join52(outputDir, "assets")).filter(
|
|
81975
82524
|
(f3) => /\.(png|jpg|jpeg|webp|gif)$/i.test(f3)
|
|
81976
82525
|
);
|
|
81977
82526
|
const model = process.env.HYPERFRAMES_GEMINI_MODEL || "gemini-3.1-flash-lite-preview";
|
|
@@ -81980,8 +82529,8 @@ async function captionImagesWithGemini(outputDir, progress, warnings) {
|
|
|
81980
82529
|
const batch = imageFiles.slice(i2, i2 + BATCH_SIZE);
|
|
81981
82530
|
const results = await Promise.allSettled(
|
|
81982
82531
|
batch.map(async (file) => {
|
|
81983
|
-
const filePath =
|
|
81984
|
-
const stat3 =
|
|
82532
|
+
const filePath = join52(outputDir, "assets", file);
|
|
82533
|
+
const stat3 = statSync20(filePath);
|
|
81985
82534
|
if (stat3.size > 4e6) return { file, caption: "" };
|
|
81986
82535
|
const buffer = readFileSync36(filePath);
|
|
81987
82536
|
const base64 = buffer.toString("base64");
|
|
@@ -82029,12 +82578,12 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
82029
82578
|
const uncaptionedLines = [];
|
|
82030
82579
|
const svgLines = [];
|
|
82031
82580
|
const fontLines = [];
|
|
82032
|
-
const assetsPath =
|
|
82581
|
+
const assetsPath = join52(outputDir, "assets");
|
|
82033
82582
|
try {
|
|
82034
|
-
for (const file of
|
|
82583
|
+
for (const file of readdirSync19(assetsPath)) {
|
|
82035
82584
|
if (file === "svgs" || file === "fonts" || file === "lottie" || file === "videos") continue;
|
|
82036
|
-
const filePath =
|
|
82037
|
-
const stat3 =
|
|
82585
|
+
const filePath = join52(assetsPath, file);
|
|
82586
|
+
const stat3 = statSync20(filePath);
|
|
82038
82587
|
if (!stat3.isFile()) continue;
|
|
82039
82588
|
const sizeKb = Math.round(stat3.size / 1024);
|
|
82040
82589
|
const catalogMatch = catalogedAssets.find(
|
|
@@ -82062,8 +82611,8 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
82062
82611
|
} catch {
|
|
82063
82612
|
}
|
|
82064
82613
|
try {
|
|
82065
|
-
const svgsPath =
|
|
82066
|
-
for (const file of
|
|
82614
|
+
const svgsPath = join52(assetsPath, "svgs");
|
|
82615
|
+
for (const file of readdirSync19(svgsPath)) {
|
|
82067
82616
|
if (!file.endsWith(".svg")) continue;
|
|
82068
82617
|
const svgMatch = tokens.svgs.find(
|
|
82069
82618
|
(s2) => s2.label && file.includes(
|
|
@@ -82077,8 +82626,8 @@ function generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCap
|
|
|
82077
82626
|
} catch {
|
|
82078
82627
|
}
|
|
82079
82628
|
try {
|
|
82080
|
-
const fontsPath =
|
|
82081
|
-
for (const file of
|
|
82629
|
+
const fontsPath = join52(assetsPath, "fonts");
|
|
82630
|
+
for (const file of readdirSync19(fontsPath)) {
|
|
82082
82631
|
fontLines.push(`fonts/${file} \u2014 font file`);
|
|
82083
82632
|
}
|
|
82084
82633
|
} catch {
|
|
@@ -82096,13 +82645,13 @@ var agentPromptGenerator_exports = {};
|
|
|
82096
82645
|
__export(agentPromptGenerator_exports, {
|
|
82097
82646
|
generateAgentPrompt: () => generateAgentPrompt
|
|
82098
82647
|
});
|
|
82099
|
-
import { writeFileSync as
|
|
82100
|
-
import { join as
|
|
82648
|
+
import { writeFileSync as writeFileSync21 } from "fs";
|
|
82649
|
+
import { join as join53 } from "path";
|
|
82101
82650
|
function generateAgentPrompt(outputDir, url, tokens, _animations, hasScreenshot, hasLottie, hasShaders, _catalogedAssets, detectedLibraries) {
|
|
82102
82651
|
const prompt = buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries);
|
|
82103
|
-
|
|
82104
|
-
|
|
82105
|
-
|
|
82652
|
+
writeFileSync21(join53(outputDir, "AGENTS.md"), prompt, "utf-8");
|
|
82653
|
+
writeFileSync21(join53(outputDir, "CLAUDE.md"), prompt, "utf-8");
|
|
82654
|
+
writeFileSync21(join53(outputDir, ".cursorrules"), prompt, "utf-8");
|
|
82106
82655
|
}
|
|
82107
82656
|
function buildPrompt(url, tokens, hasScreenshot, hasLottie, hasShaders, detectedLibraries) {
|
|
82108
82657
|
const title = tokens.title || new URL(url).hostname.replace(/^www\./, "");
|
|
@@ -82169,8 +82718,8 @@ var init_agentPromptGenerator = __esm({
|
|
|
82169
82718
|
});
|
|
82170
82719
|
|
|
82171
82720
|
// src/capture/scaffolding.ts
|
|
82172
|
-
import { existsSync as
|
|
82173
|
-
import { join as
|
|
82721
|
+
import { existsSync as existsSync50, writeFileSync as writeFileSync22, readFileSync as readFileSync37 } from "fs";
|
|
82722
|
+
import { join as join54, resolve as resolve36 } from "path";
|
|
82174
82723
|
function loadEnvFile(startDir) {
|
|
82175
82724
|
try {
|
|
82176
82725
|
let dir = resolve36(startDir);
|
|
@@ -82196,10 +82745,10 @@ function loadEnvFile(startDir) {
|
|
|
82196
82745
|
}
|
|
82197
82746
|
}
|
|
82198
82747
|
async function generateProjectScaffold(outputDir, url, tokens, animationCatalog, hasScreenshots, hasLotties, hasShaders, catalogedAssets, progress, warnings, detectedLibraries) {
|
|
82199
|
-
const metaPath =
|
|
82200
|
-
if (!
|
|
82748
|
+
const metaPath = join54(outputDir, "meta.json");
|
|
82749
|
+
if (!existsSync50(metaPath)) {
|
|
82201
82750
|
const hostname = new URL(url).hostname.replace(/^www\./, "");
|
|
82202
|
-
|
|
82751
|
+
writeFileSync22(
|
|
82203
82752
|
metaPath,
|
|
82204
82753
|
JSON.stringify({ id: hostname + "-video", name: tokens.title || hostname }, null, 2),
|
|
82205
82754
|
"utf-8"
|
|
@@ -82234,11 +82783,11 @@ var screenshotCapture_exports = {};
|
|
|
82234
82783
|
__export(screenshotCapture_exports, {
|
|
82235
82784
|
captureScrollScreenshots: () => captureScrollScreenshots
|
|
82236
82785
|
});
|
|
82237
|
-
import { writeFileSync as
|
|
82238
|
-
import { join as
|
|
82786
|
+
import { writeFileSync as writeFileSync23, mkdirSync as mkdirSync28 } from "fs";
|
|
82787
|
+
import { join as join55 } from "path";
|
|
82239
82788
|
async function captureScrollScreenshots(page, outputDir) {
|
|
82240
|
-
const screenshotsDir =
|
|
82241
|
-
|
|
82789
|
+
const screenshotsDir = join55(outputDir, "screenshots");
|
|
82790
|
+
mkdirSync28(screenshotsDir, { recursive: true });
|
|
82242
82791
|
const MAX_SCREENSHOTS = 20;
|
|
82243
82792
|
const filePaths = [];
|
|
82244
82793
|
try {
|
|
@@ -82271,9 +82820,9 @@ async function captureScrollScreenshots(page, outputDir) {
|
|
|
82271
82820
|
finalPositions[i2] / Math.max(1, scrollHeight - viewportHeight) * 100
|
|
82272
82821
|
);
|
|
82273
82822
|
const filename = `scroll-${String(Math.min(pct, 100)).padStart(3, "0")}.png`;
|
|
82274
|
-
const filePath =
|
|
82823
|
+
const filePath = join55(screenshotsDir, filename);
|
|
82275
82824
|
const buffer = await page.screenshot({ type: "png" });
|
|
82276
|
-
|
|
82825
|
+
writeFileSync23(filePath, buffer);
|
|
82277
82826
|
filePaths.push(`screenshots/${filename}`);
|
|
82278
82827
|
}
|
|
82279
82828
|
await page.evaluate(`window.scrollTo(0, 0)`);
|
|
@@ -82584,8 +83133,8 @@ var capture_exports = {};
|
|
|
82584
83133
|
__export(capture_exports, {
|
|
82585
83134
|
captureWebsite: () => captureWebsite
|
|
82586
83135
|
});
|
|
82587
|
-
import { mkdirSync as
|
|
82588
|
-
import { join as
|
|
83136
|
+
import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync24, existsSync as existsSync51 } from "fs";
|
|
83137
|
+
import { join as join56 } from "path";
|
|
82589
83138
|
async function captureWebsite(opts, onProgress) {
|
|
82590
83139
|
const {
|
|
82591
83140
|
url,
|
|
@@ -82602,9 +83151,9 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82602
83151
|
onProgress?.(stage, detail);
|
|
82603
83152
|
};
|
|
82604
83153
|
loadEnvFile(outputDir);
|
|
82605
|
-
|
|
82606
|
-
|
|
82607
|
-
|
|
83154
|
+
mkdirSync29(join56(outputDir, "extracted"), { recursive: true });
|
|
83155
|
+
mkdirSync29(join56(outputDir, "screenshots"), { recursive: true });
|
|
83156
|
+
mkdirSync29(join56(outputDir, "assets"), { recursive: true });
|
|
82608
83157
|
progress("browser", "Launching headless Chrome...");
|
|
82609
83158
|
const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
|
|
82610
83159
|
const browser = await ensureBrowser2();
|
|
@@ -82760,8 +83309,8 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82760
83309
|
} catch {
|
|
82761
83310
|
}
|
|
82762
83311
|
if (discoveredLotties.length > 0) {
|
|
82763
|
-
const lottieDir =
|
|
82764
|
-
|
|
83312
|
+
const lottieDir = join56(outputDir, "assets", "lottie");
|
|
83313
|
+
mkdirSync29(lottieDir, { recursive: true });
|
|
82765
83314
|
const savedCount = await saveLottieAnimations(discoveredLotties, lottieDir);
|
|
82766
83315
|
if (savedCount > 0) {
|
|
82767
83316
|
await renderLottiePreviews(chromeBrowser, lottieDir, outputDir);
|
|
@@ -82779,8 +83328,8 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82779
83328
|
return true;
|
|
82780
83329
|
});
|
|
82781
83330
|
capturedShaders = unique;
|
|
82782
|
-
|
|
82783
|
-
|
|
83331
|
+
writeFileSync24(
|
|
83332
|
+
join56(outputDir, "extracted", "shaders.json"),
|
|
82784
83333
|
JSON.stringify(unique, null, 2),
|
|
82785
83334
|
"utf-8"
|
|
82786
83335
|
);
|
|
@@ -82790,8 +83339,8 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82790
83339
|
}
|
|
82791
83340
|
progress("tokens", "Extracting design tokens...");
|
|
82792
83341
|
const tokens = await extractTokens(page1);
|
|
82793
|
-
|
|
82794
|
-
|
|
83342
|
+
writeFileSync24(
|
|
83343
|
+
join56(outputDir, "extracted", "tokens.json"),
|
|
82795
83344
|
JSON.stringify(tokens, null, 2),
|
|
82796
83345
|
"utf-8"
|
|
82797
83346
|
);
|
|
@@ -82864,8 +83413,8 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82864
83413
|
scrollTriggeredElements: (animationCatalog.scrollTargets || []).length,
|
|
82865
83414
|
representativeAnimations: representativeAnims
|
|
82866
83415
|
};
|
|
82867
|
-
|
|
82868
|
-
|
|
83416
|
+
writeFileSync24(
|
|
83417
|
+
join56(outputDir, "extracted", "animations.json"),
|
|
82869
83418
|
JSON.stringify(leanCatalog, null, 2),
|
|
82870
83419
|
"utf-8"
|
|
82871
83420
|
);
|
|
@@ -82876,18 +83425,18 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82876
83425
|
assets = await downloadAssets(tokens, outputDir, catalogedAssets, faviconLinks);
|
|
82877
83426
|
}
|
|
82878
83427
|
if (visibleTextContent) {
|
|
82879
|
-
|
|
83428
|
+
writeFileSync24(join56(outputDir, "extracted", "visible-text.txt"), visibleTextContent, "utf-8");
|
|
82880
83429
|
}
|
|
82881
83430
|
if (catalogedAssets.length > 0) {
|
|
82882
|
-
|
|
82883
|
-
|
|
83431
|
+
writeFileSync24(
|
|
83432
|
+
join56(outputDir, "extracted", "assets-catalog.json"),
|
|
82884
83433
|
JSON.stringify(catalogedAssets, null, 2),
|
|
82885
83434
|
"utf-8"
|
|
82886
83435
|
);
|
|
82887
83436
|
}
|
|
82888
83437
|
if (detectedLibraries.length > 0) {
|
|
82889
|
-
|
|
82890
|
-
|
|
83438
|
+
writeFileSync24(
|
|
83439
|
+
join56(outputDir, "extracted", "detected-libraries.json"),
|
|
82891
83440
|
JSON.stringify(detectedLibraries, null, 2),
|
|
82892
83441
|
"utf-8"
|
|
82893
83442
|
);
|
|
@@ -82897,8 +83446,8 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82897
83446
|
try {
|
|
82898
83447
|
const lines = generateAssetDescriptions(outputDir, tokens, catalogedAssets, geminiCaptions);
|
|
82899
83448
|
if (lines.length > 0) {
|
|
82900
|
-
|
|
82901
|
-
|
|
83449
|
+
writeFileSync24(
|
|
83450
|
+
join56(outputDir, "extracted", "asset-descriptions.md"),
|
|
82902
83451
|
"# Asset Descriptions\n\nOne line per file. Read this instead of opening every image individually.\n\n" + lines.map((l) => "- " + l).join("\n") + "\n",
|
|
82903
83452
|
"utf-8"
|
|
82904
83453
|
);
|
|
@@ -82914,7 +83463,7 @@ async function captureWebsite(opts, onProgress) {
|
|
|
82914
83463
|
animationCatalog,
|
|
82915
83464
|
screenshots.length > 0,
|
|
82916
83465
|
discoveredLotties.length > 0,
|
|
82917
|
-
|
|
83466
|
+
existsSync51(join56(outputDir, "extracted", "shaders.json")),
|
|
82918
83467
|
catalogedAssets,
|
|
82919
83468
|
progress,
|
|
82920
83469
|
warnings,
|
|
@@ -83093,11 +83642,11 @@ var init_capture2 = __esm({
|
|
|
83093
83642
|
} catch (err) {
|
|
83094
83643
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
83095
83644
|
try {
|
|
83096
|
-
const { mkdirSync:
|
|
83097
|
-
|
|
83645
|
+
const { mkdirSync: mkdirSync31, writeFileSync: writeFileSync25 } = await import("fs");
|
|
83646
|
+
mkdirSync31(outputDir, { recursive: true });
|
|
83098
83647
|
const isTimeout = /timeout|timed out/i.test(errMsg);
|
|
83099
83648
|
const reason = isTimeout ? "Page navigation timed out \u2014 the site may be blocking headless browsers or requires authentication." : `Capture failed: ${errMsg}`;
|
|
83100
|
-
|
|
83649
|
+
writeFileSync25(
|
|
83101
83650
|
`${outputDir}/BLOCKED.md`,
|
|
83102
83651
|
`# Capture Failed
|
|
83103
83652
|
|
|
@@ -83261,9 +83810,9 @@ __export(autoUpdate_exports, {
|
|
|
83261
83810
|
scheduleBackgroundInstall: () => scheduleBackgroundInstall
|
|
83262
83811
|
});
|
|
83263
83812
|
import { spawn as spawn12 } from "child_process";
|
|
83264
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
83813
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync30, openSync } from "fs";
|
|
83265
83814
|
import { homedir as homedir10 } from "os";
|
|
83266
|
-
import { join as
|
|
83815
|
+
import { join as join57 } from "path";
|
|
83267
83816
|
import { compareVersions as compareVersions2 } from "compare-versions";
|
|
83268
83817
|
function isAutoInstallDisabled() {
|
|
83269
83818
|
if (isDevMode()) return true;
|
|
@@ -83278,15 +83827,15 @@ function majorOf(version) {
|
|
|
83278
83827
|
}
|
|
83279
83828
|
function log(line) {
|
|
83280
83829
|
try {
|
|
83281
|
-
|
|
83830
|
+
mkdirSync30(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
83282
83831
|
appendFileSync2(LOG_FILE, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
83283
83832
|
`, { mode: 384 });
|
|
83284
83833
|
} catch {
|
|
83285
83834
|
}
|
|
83286
83835
|
}
|
|
83287
83836
|
function launchDetachedInstall(installCommand, version) {
|
|
83288
|
-
|
|
83289
|
-
const configFile =
|
|
83837
|
+
mkdirSync30(CONFIG_DIR2, { recursive: true, mode: 448 });
|
|
83838
|
+
const configFile = join57(CONFIG_DIR2, "config.json");
|
|
83290
83839
|
const nodeScript = `
|
|
83291
83840
|
const { exec } = require("node:child_process");
|
|
83292
83841
|
const { readFileSync, renameSync, writeFileSync } = require("node:fs");
|
|
@@ -83405,8 +83954,8 @@ var init_autoUpdate = __esm({
|
|
|
83405
83954
|
init_config();
|
|
83406
83955
|
init_env();
|
|
83407
83956
|
init_installerDetection();
|
|
83408
|
-
CONFIG_DIR2 =
|
|
83409
|
-
LOG_FILE =
|
|
83957
|
+
CONFIG_DIR2 = join57(homedir10(), ".hyperframes");
|
|
83958
|
+
LOG_FILE = join57(CONFIG_DIR2, "auto-update.log");
|
|
83410
83959
|
PENDING_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
83411
83960
|
}
|
|
83412
83961
|
});
|