hyperframes 0.7.8 → 0.7.10
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 +148 -10
- package/dist/studio/assets/{index-BSyZKYmH.js → index-CWTSv_j9.js} +99 -99
- package/dist/studio/assets/{index-Dgeszckd.js → index-ClZvune4.js} +1 -1
- package/dist/studio/assets/{index--lOAjFJl.js → index-TqAN2o6c.js} +1 -1
- package/dist/studio/index.html +1 -1
- package/dist/studio/index.js +16 -9
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -50,7 +50,7 @@ var VERSION;
|
|
|
50
50
|
var init_version = __esm({
|
|
51
51
|
"src/version.ts"() {
|
|
52
52
|
"use strict";
|
|
53
|
-
VERSION = true ? "0.7.
|
|
53
|
+
VERSION = true ? "0.7.10" : "0.0.0-dev";
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -21074,6 +21074,10 @@ function resolveTimelinePositions(anims) {
|
|
|
21074
21074
|
let cursor = 0;
|
|
21075
21075
|
let prevStart = 0;
|
|
21076
21076
|
for (const anim of anims) {
|
|
21077
|
+
if (anim.method === "set" && anim.global) {
|
|
21078
|
+
anim.resolvedStart = 0;
|
|
21079
|
+
continue;
|
|
21080
|
+
}
|
|
21077
21081
|
const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
|
|
21078
21082
|
let start;
|
|
21079
21083
|
if (anim.implicitPosition) {
|
|
@@ -25380,11 +25384,40 @@ function describeStudioElement(tag) {
|
|
|
25380
25384
|
parts.push(">");
|
|
25381
25385
|
return parts.join("");
|
|
25382
25386
|
}
|
|
25383
|
-
|
|
25387
|
+
function findCodeFenceLeak(headWithoutValidBlocks) {
|
|
25388
|
+
return MARKDOWN_CODE_FENCE_PATTERN.exec(headWithoutValidBlocks)?.[0] ?? null;
|
|
25389
|
+
}
|
|
25390
|
+
function findOrphanCssLeak(headContent) {
|
|
25391
|
+
const residualText = headContent.replace(HEAD_BLOCKS_TO_IGNORE_PATTERN, " ").replace(HTML_TAG_PATTERN, " ");
|
|
25392
|
+
return ORPHAN_CSS_AT_RULE_PATTERN.exec(residualText)?.[0] ?? ORPHAN_CSS_RULE_PATTERN.exec(residualText)?.[0] ?? null;
|
|
25393
|
+
}
|
|
25394
|
+
function findStrayCloseLeak(headWithoutValidBlocks) {
|
|
25395
|
+
return STRAY_HEAD_CLOSE_PATTERN.exec(headWithoutValidBlocks)?.[0] ?? null;
|
|
25396
|
+
}
|
|
25397
|
+
function findLeakedTextInHeadContent(headContent) {
|
|
25398
|
+
const withoutValidBlocks = headContent.replace(HEAD_BLOCKS_TO_IGNORE_PATTERN, " ");
|
|
25399
|
+
return findCodeFenceLeak(withoutValidBlocks) ?? findOrphanCssLeak(headContent) ?? findStrayCloseLeak(withoutValidBlocks);
|
|
25400
|
+
}
|
|
25401
|
+
function findLeakedTextInHead(rawSource) {
|
|
25402
|
+
const headMatches = [...rawSource.matchAll(HEAD_CONTENT_PATTERN)];
|
|
25403
|
+
for (const match of headMatches) {
|
|
25404
|
+
const leakedText = findLeakedTextInHeadContent(match[1] ?? "");
|
|
25405
|
+
if (leakedText) return leakedText;
|
|
25406
|
+
}
|
|
25407
|
+
return null;
|
|
25408
|
+
}
|
|
25409
|
+
var HEAD_BLOCKS_TO_IGNORE_PATTERN, HTML_TAG_PATTERN, HEAD_CONTENT_PATTERN, STRAY_HEAD_CLOSE_PATTERN, MARKDOWN_CODE_FENCE_PATTERN, ORPHAN_CSS_AT_RULE_PATTERN, ORPHAN_CSS_RULE_PATTERN, coreRules;
|
|
25384
25410
|
var init_core = __esm({
|
|
25385
25411
|
"../core/src/lint/rules/core.ts"() {
|
|
25386
25412
|
"use strict";
|
|
25387
25413
|
init_utils2();
|
|
25414
|
+
HEAD_BLOCKS_TO_IGNORE_PATTERN = /<(?:style|script|template|title|noscript)\b[^>]*>[\s\S]*?<\/(?:style|script|template|title|noscript)(?:\s[^>]*)?>/gi;
|
|
25415
|
+
HTML_TAG_PATTERN = /<[^>]+>/g;
|
|
25416
|
+
HEAD_CONTENT_PATTERN = /<head\b[^>]*>([\s\S]*?)(?:<\/head>|<body\b|$)/gi;
|
|
25417
|
+
STRAY_HEAD_CLOSE_PATTERN = /<\/(?:style|script)(?:\s[^>]*)?>/i;
|
|
25418
|
+
MARKDOWN_CODE_FENCE_PATTERN = /```[^\r\n`]*(?:\r?\n|$)[\s\S]*?```/i;
|
|
25419
|
+
ORPHAN_CSS_AT_RULE_PATTERN = /(?:^|\s)@(?:container|font-face|keyframes|layer|media|page|property|scope|supports)[^{<]*\{[\s\S]*?:[\s\S]*?\}/i;
|
|
25420
|
+
ORPHAN_CSS_RULE_PATTERN = /(?:^|\s)(?:\/\*[\s\S]*?\*\/\s*)?(?:@[a-z-]+[^{}<]*|[.#][\w-]+[^{}<]*|[a-z][\w-]*(?:\s+[.#:[\w-][^{}<]*)?)\s*\{[^{}]*:[^{}]*\}/i;
|
|
25388
25421
|
coreRules = [
|
|
25389
25422
|
// root_missing_composition_id + root_missing_dimensions
|
|
25390
25423
|
({ rootTag }) => {
|
|
@@ -25411,6 +25444,20 @@ var init_core = __esm({
|
|
|
25411
25444
|
}
|
|
25412
25445
|
return findings;
|
|
25413
25446
|
},
|
|
25447
|
+
// head_leaked_text
|
|
25448
|
+
({ source }) => {
|
|
25449
|
+
const snippet = findLeakedTextInHead(source);
|
|
25450
|
+
if (!snippet) return [];
|
|
25451
|
+
return [
|
|
25452
|
+
{
|
|
25453
|
+
code: "head_leaked_text",
|
|
25454
|
+
severity: "error",
|
|
25455
|
+
message: "Detected leaked code or CSS text in the document `<head>`. Browsers render this as visible text in the video.",
|
|
25456
|
+
fixHint: "Move CSS into a single `<style>...</style>` block and remove stray close tags, markdown fences, or code text from `<head>`.",
|
|
25457
|
+
snippet: truncateSnippet(snippet)
|
|
25458
|
+
}
|
|
25459
|
+
];
|
|
25460
|
+
},
|
|
25414
25461
|
// missing_timeline_registry + timeline_registry_missing_init
|
|
25415
25462
|
({ source, rawSource, options }) => {
|
|
25416
25463
|
if (options.isSubComposition || rawSource.trimStart().toLowerCase().startsWith("<template")) {
|
|
@@ -32424,6 +32471,7 @@ __export(src_exports, {
|
|
|
32424
32471
|
HYPERFRAME_RUNTIME_GLOBALS: () => HYPERFRAME_RUNTIME_GLOBALS,
|
|
32425
32472
|
ITEM_TYPES: () => ITEM_TYPES,
|
|
32426
32473
|
ITEM_TYPE_DIRS: () => ITEM_TYPE_DIRS,
|
|
32474
|
+
MEDIA_DURATION_CLAMP_EPSILON_SECONDS: () => MEDIA_DURATION_CLAMP_EPSILON_SECONDS,
|
|
32427
32475
|
MEDIA_STYLES: () => MEDIA_STYLES,
|
|
32428
32476
|
MEDIA_VISUAL_STYLE_PROPERTIES: () => MEDIA_VISUAL_STYLE_PROPERTIES,
|
|
32429
32477
|
TEXT_STYLES: () => TEXT_STYLES,
|
|
@@ -39113,6 +39161,15 @@ function getFrameAtTime(extracted, globalTime, videoStart, loop = false, mediaSt
|
|
|
39113
39161
|
if (frameIndex < 0 || frameIndex >= extracted.totalFrames) return null;
|
|
39114
39162
|
return extracted.framePaths.get(frameIndex) || null;
|
|
39115
39163
|
}
|
|
39164
|
+
function analyzeClipMediaFit(params) {
|
|
39165
|
+
const { slotSeconds, mediaSeconds, loop } = params;
|
|
39166
|
+
if (loop) return null;
|
|
39167
|
+
if (!(slotSeconds > 0) || !Number.isFinite(mediaSeconds) || mediaSeconds < 0) return null;
|
|
39168
|
+
const toleranceSeconds = MEDIA_DURATION_CLAMP_EPSILON_SECONDS;
|
|
39169
|
+
const shortfallSeconds = slotSeconds - mediaSeconds;
|
|
39170
|
+
if (shortfallSeconds <= toleranceSeconds) return null;
|
|
39171
|
+
return { shortfallSeconds, toleranceSeconds };
|
|
39172
|
+
}
|
|
39116
39173
|
function createFrameLookupTable(videos, extracted) {
|
|
39117
39174
|
const table = new FrameLookupTable();
|
|
39118
39175
|
const extractedMap = /* @__PURE__ */ new Map();
|
|
@@ -39123,7 +39180,7 @@ function createFrameLookupTable(videos, extracted) {
|
|
|
39123
39180
|
}
|
|
39124
39181
|
return table;
|
|
39125
39182
|
}
|
|
39126
|
-
var VIDEO_FRAME_FORMATS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
|
|
39183
|
+
var VIDEO_FRAME_FORMATS, ALPHA_CAPABLE_CODECS, HOLD_LAST_FRAME_TOLERANCE_FRAMES, FrameLookupTable;
|
|
39127
39184
|
var init_videoFrameExtractor = __esm({
|
|
39128
39185
|
"../engine/src/services/videoFrameExtractor.ts"() {
|
|
39129
39186
|
"use strict";
|
|
@@ -39140,6 +39197,7 @@ var init_videoFrameExtractor = __esm({
|
|
|
39140
39197
|
init_extractionCache();
|
|
39141
39198
|
VIDEO_FRAME_FORMATS = ["auto", "jpg", "png"];
|
|
39142
39199
|
ALPHA_CAPABLE_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8", "prores"]);
|
|
39200
|
+
HOLD_LAST_FRAME_TOLERANCE_FRAMES = 2;
|
|
39143
39201
|
FrameLookupTable = class {
|
|
39144
39202
|
videos = /* @__PURE__ */ new Map();
|
|
39145
39203
|
orderedVideos = [];
|
|
@@ -39218,7 +39276,12 @@ var init_videoFrameExtractor = __esm({
|
|
|
39218
39276
|
continue;
|
|
39219
39277
|
}
|
|
39220
39278
|
if (frameIndex < 0 || frameIndex >= video.extracted.totalFrames) {
|
|
39221
|
-
|
|
39279
|
+
const fps = video.extracted.fps;
|
|
39280
|
+
const holdTolerance = Math.max(
|
|
39281
|
+
fps > 0 ? HOLD_LAST_FRAME_TOLERANCE_FRAMES / fps : 0,
|
|
39282
|
+
MEDIA_DURATION_CLAMP_EPSILON_SECONDS
|
|
39283
|
+
);
|
|
39284
|
+
if (globalTime >= video.end - holdTolerance && video.extracted.totalFrames > 0) {
|
|
39222
39285
|
const lastIndex = video.extracted.totalFrames - 1;
|
|
39223
39286
|
const lastPath = video.extracted.framePaths.get(lastIndex);
|
|
39224
39287
|
if (lastPath) frames.set(videoId, { framePath: lastPath, frameIndex: lastIndex });
|
|
@@ -42099,6 +42162,7 @@ __export(src_exports2, {
|
|
|
42099
42162
|
TRANSITIONS: () => TRANSITIONS,
|
|
42100
42163
|
VIDEO_FRAME_FORMATS: () => VIDEO_FRAME_FORMATS,
|
|
42101
42164
|
acquireBrowser: () => acquireBrowser,
|
|
42165
|
+
analyzeClipMediaFit: () => analyzeClipMediaFit,
|
|
42102
42166
|
analyzeCompositionHdr: () => analyzeCompositionHdr,
|
|
42103
42167
|
analyzeKeyframeIntervals: () => analyzeKeyframeIntervals,
|
|
42104
42168
|
applyDomLayerMask: () => applyDomLayerMask,
|
|
@@ -84503,6 +84567,10 @@ function resolveTimelinePositions2(anims) {
|
|
|
84503
84567
|
let cursor = 0;
|
|
84504
84568
|
let prevStart = 0;
|
|
84505
84569
|
for (const anim of anims) {
|
|
84570
|
+
if (anim.method === "set" && anim.global) {
|
|
84571
|
+
anim.resolvedStart = 0;
|
|
84572
|
+
continue;
|
|
84573
|
+
}
|
|
84506
84574
|
const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION2;
|
|
84507
84575
|
let start;
|
|
84508
84576
|
if (anim.implicitPosition) {
|
|
@@ -95590,7 +95658,7 @@ async function resolveMediaDuration(src, mediaStart, baseDir, downloadDir, tagNa
|
|
|
95590
95658
|
const duration = effectiveDuration > 0 ? effectiveDuration : fileDuration;
|
|
95591
95659
|
return { duration, resolvedPath: filePath };
|
|
95592
95660
|
}
|
|
95593
|
-
async function compileHtmlFile(html, baseDir, downloadDir) {
|
|
95661
|
+
async function compileHtmlFile(html, baseDir, downloadDir, log2) {
|
|
95594
95662
|
const { html: staticCompiled, unresolved } = compileTimingAttrs(html);
|
|
95595
95663
|
const mediaUnresolved = unresolved.filter(
|
|
95596
95664
|
(el) => (el.tagName === "video" || el.tagName === "audio") && el.src
|
|
@@ -95615,13 +95683,17 @@ async function compileHtmlFile(html, baseDir, downloadDir) {
|
|
|
95615
95683
|
downloadDir,
|
|
95616
95684
|
el.tagName
|
|
95617
95685
|
);
|
|
95618
|
-
return { id: el.id, duration: el.duration, maxDuration, src: el.src };
|
|
95686
|
+
return { id: el.id, tagName: el.tagName, duration: el.duration, maxDuration, src: el.src };
|
|
95619
95687
|
})
|
|
95620
95688
|
);
|
|
95621
95689
|
const clampList = [];
|
|
95622
95690
|
for (const r2 of clampResults) {
|
|
95623
95691
|
if (r2.maxDuration > 0 && shouldClampMediaDuration(r2.duration, r2.maxDuration)) {
|
|
95624
95692
|
clampList.push({ id: r2.id, duration: r2.maxDuration });
|
|
95693
|
+
const kind = r2.tagName === "audio" ? "Audio" : "Video";
|
|
95694
|
+
log2?.warn(
|
|
95695
|
+
`[compile] ${kind} "${r2.id}" (${r2.src}) is ${r2.maxDuration.toFixed(2)}s but its data-duration is ${r2.duration.toFixed(2)}s \u2014 the slot is shortened to the media length. Set data-duration to ~${r2.maxDuration.toFixed(2)}s, trim data-media-start, or use a longer/looping source if that isn't intended.`
|
|
95696
|
+
);
|
|
95625
95697
|
}
|
|
95626
95698
|
}
|
|
95627
95699
|
if (clampList.length > 0) {
|
|
@@ -96310,7 +96382,8 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
|
|
|
96310
96382
|
const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
|
|
96311
96383
|
rawHtml,
|
|
96312
96384
|
projectDir,
|
|
96313
|
-
downloadDir
|
|
96385
|
+
downloadDir,
|
|
96386
|
+
options.log
|
|
96314
96387
|
);
|
|
96315
96388
|
const {
|
|
96316
96389
|
videos: subVideos,
|
|
@@ -96709,6 +96782,7 @@ async function runCompileStage(input2) {
|
|
|
96709
96782
|
} = input2;
|
|
96710
96783
|
const compileStart = Date.now();
|
|
96711
96784
|
const compiled = await compileForRender(projectDir, htmlPath, join43(workDir, "downloads"), {
|
|
96785
|
+
log: log2,
|
|
96712
96786
|
failClosedFontFetch: failClosedFontFetch === true,
|
|
96713
96787
|
allowSystemFontCapture,
|
|
96714
96788
|
animatedGifCacheDir: cfg.extractCacheDir ? join43(cfg.extractCacheDir, "animated-gif") : void 0,
|
|
@@ -101912,7 +101986,18 @@ function validateNoSystemFonts(compiledHtml) {
|
|
|
101912
101986
|
);
|
|
101913
101987
|
}
|
|
101914
101988
|
}
|
|
101915
|
-
|
|
101989
|
+
function validateDistributedDuration(input2) {
|
|
101990
|
+
const { duration, totalFrames, fps } = input2;
|
|
101991
|
+
const maxFrames = Math.ceil(MAX_DISTRIBUTED_DURATION_SECONDS * fps);
|
|
101992
|
+
if (Number.isFinite(duration) && duration > 0 && Number.isFinite(fps) && fps > 0 && Number.isSafeInteger(totalFrames) && totalFrames > 0 && totalFrames <= maxFrames) {
|
|
101993
|
+
return;
|
|
101994
|
+
}
|
|
101995
|
+
throw new PlanValidationError(
|
|
101996
|
+
DISTRIBUTED_DURATION_OUT_OF_RANGE,
|
|
101997
|
+
`[planValidation] Distributed render duration is out of range: duration=${String(duration)}s totalFrames=${String(totalFrames)} fps=${String(fps)} (maxDuration=${String(MAX_DISTRIBUTED_DURATION_SECONDS)}s, maxFrames=${String(maxFrames)}). This usually means an unbounded timeline escaped into render planning, such as GSAP repeat:-1 / yoyo loops without an explicit finite root duration. Add a finite data-duration or replace infinite repeats with a finite repeat count before rendering.`
|
|
101998
|
+
);
|
|
101999
|
+
}
|
|
102000
|
+
var PlanValidationError, SYSTEM_FONT_USED, DISTRIBUTED_DURATION_OUT_OF_RANGE, MAX_DISTRIBUTED_DURATION_SECONDS;
|
|
101916
102001
|
var init_planValidation = __esm({
|
|
101917
102002
|
"../producer/src/services/render/planValidation.ts"() {
|
|
101918
102003
|
"use strict";
|
|
@@ -101929,6 +102014,8 @@ var init_planValidation = __esm({
|
|
|
101929
102014
|
}
|
|
101930
102015
|
};
|
|
101931
102016
|
SYSTEM_FONT_USED = "SYSTEM_FONT_USED";
|
|
102017
|
+
DISTRIBUTED_DURATION_OUT_OF_RANGE = "DISTRIBUTED_DURATION_OUT_OF_RANGE";
|
|
102018
|
+
MAX_DISTRIBUTED_DURATION_SECONDS = 24 * 60 * 60;
|
|
101932
102019
|
}
|
|
101933
102020
|
});
|
|
101934
102021
|
|
|
@@ -102314,6 +102401,11 @@ async function plan(projectDir, config, planDir) {
|
|
|
102314
102401
|
job.duration = probeResult.duration;
|
|
102315
102402
|
job.totalFrames = probeResult.totalFrames;
|
|
102316
102403
|
const totalFrames = probeResult.totalFrames;
|
|
102404
|
+
validateDistributedDuration({
|
|
102405
|
+
duration: probeResult.duration,
|
|
102406
|
+
totalFrames,
|
|
102407
|
+
fps: fpsToNumber(job.config.fps)
|
|
102408
|
+
});
|
|
102317
102409
|
if (probeResult.fileServer) closeFileServerSafely(probeResult.fileServer, "plan", log2);
|
|
102318
102410
|
if (probeResult.probeSession) {
|
|
102319
102411
|
try {
|
|
@@ -102456,6 +102548,7 @@ var PLAN_PROJECT_DIR_SKIP_SEGMENTS, DEFAULT_MAX_PARALLEL_CHUNKS, MIN_CHUNK_SIZE,
|
|
|
102456
102548
|
var init_plan = __esm({
|
|
102457
102549
|
"../producer/src/services/distributed/plan.ts"() {
|
|
102458
102550
|
"use strict";
|
|
102551
|
+
init_src();
|
|
102459
102552
|
init_src2();
|
|
102460
102553
|
init_logger();
|
|
102461
102554
|
init_fileServer2();
|
|
@@ -102531,7 +102624,7 @@ function rebuildExtractedFramesFromPlanDir(planDir, videos) {
|
|
|
102531
102624
|
for (let i2 = 0; i2 < frames.length; i2++) {
|
|
102532
102625
|
const frameName = frames[i2];
|
|
102533
102626
|
if (!frameName) continue;
|
|
102534
|
-
framePaths.set(i2
|
|
102627
|
+
framePaths.set(i2, join61(outputDir, frameName));
|
|
102535
102628
|
}
|
|
102536
102629
|
result.push({
|
|
102537
102630
|
videoId: v2.videoId,
|
|
@@ -103471,6 +103564,7 @@ var init_distributed = __esm({
|
|
|
103471
103564
|
init_assemble();
|
|
103472
103565
|
init_renderConfigValidation();
|
|
103473
103566
|
init_projectHash();
|
|
103567
|
+
init_planValidation();
|
|
103474
103568
|
}
|
|
103475
103569
|
});
|
|
103476
103570
|
|
|
@@ -113556,6 +113650,47 @@ async function seekTo2(page, time) {
|
|
|
113556
113650
|
}, time);
|
|
113557
113651
|
await new Promise((r2) => setTimeout(r2, SEEK_SETTLE_MS2));
|
|
113558
113652
|
}
|
|
113653
|
+
async function auditClipDurations(page, analyzeClipMediaFit2) {
|
|
113654
|
+
const clips = await page.evaluate(() => {
|
|
113655
|
+
const rows = [];
|
|
113656
|
+
document.querySelectorAll("video[data-duration], audio[data-duration]").forEach((node) => {
|
|
113657
|
+
const el = node;
|
|
113658
|
+
const slot = parseFloat(el.getAttribute("data-duration") ?? "");
|
|
113659
|
+
if (!(slot > 0)) return;
|
|
113660
|
+
rows.push({
|
|
113661
|
+
id: el.id || el.getAttribute("src") || `(${el.tagName.toLowerCase()})`,
|
|
113662
|
+
kind: el.tagName === "AUDIO" ? "Audio" : "Video",
|
|
113663
|
+
slot,
|
|
113664
|
+
mediaStart: parseFloat(el.getAttribute("data-media-start") ?? "0") || 0,
|
|
113665
|
+
duration: el.duration,
|
|
113666
|
+
loop: el.loop || el.getAttribute("data-loop") === "true"
|
|
113667
|
+
});
|
|
113668
|
+
});
|
|
113669
|
+
return rows;
|
|
113670
|
+
});
|
|
113671
|
+
const warnings = [];
|
|
113672
|
+
const unreadable = [];
|
|
113673
|
+
for (const clip of clips) {
|
|
113674
|
+
if (!Number.isFinite(clip.duration) || clip.duration <= 0) {
|
|
113675
|
+
unreadable.push(clip.id);
|
|
113676
|
+
continue;
|
|
113677
|
+
}
|
|
113678
|
+
const mediaSeconds = Math.max(0, clip.duration - clip.mediaStart);
|
|
113679
|
+
const fit = analyzeClipMediaFit2({ slotSeconds: clip.slot, mediaSeconds, loop: clip.loop });
|
|
113680
|
+
if (!fit) continue;
|
|
113681
|
+
warnings.push({
|
|
113682
|
+
level: "warning",
|
|
113683
|
+
text: `${clip.kind} "${clip.id}" is ${mediaSeconds.toFixed(2)}s but its slot (data-duration) is ${clip.slot.toFixed(2)}s \u2014 the slot is shortened to the media length when rendered. Set data-duration to ~${mediaSeconds.toFixed(2)}s if that isn't intended.`
|
|
113684
|
+
});
|
|
113685
|
+
}
|
|
113686
|
+
if (unreadable.length > 0) {
|
|
113687
|
+
warnings.push({
|
|
113688
|
+
level: "warning",
|
|
113689
|
+
text: `Could not read the duration of ${unreadable.length} media element(s) within the validate timeout (${unreadable.join(", ")}); their slot vs. source fit was not checked. Re-run with a longer --timeout if the source is slow to load.`
|
|
113690
|
+
});
|
|
113691
|
+
}
|
|
113692
|
+
return warnings;
|
|
113693
|
+
}
|
|
113559
113694
|
async function runContrastAudit(page) {
|
|
113560
113695
|
const duration = await getCompositionDuration3(page);
|
|
113561
113696
|
if (duration <= 0) return [];
|
|
@@ -113597,7 +113732,7 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
113597
113732
|
try {
|
|
113598
113733
|
const browser = await ensureBrowser2();
|
|
113599
113734
|
const puppeteer = await import("puppeteer-core");
|
|
113600
|
-
const { buildChromeArgs: buildChromeArgs2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
|
|
113735
|
+
const { buildChromeArgs: buildChromeArgs2, analyzeClipMediaFit: analyzeClipMediaFit2 } = await Promise.resolve().then(() => (init_src2(), src_exports2));
|
|
113601
113736
|
const browserGpuMode = process.env.PRODUCER_BROWSER_GPU_MODE === "software" ? "software" : "hardware";
|
|
113602
113737
|
const chromeBrowser = await puppeteer.default.launch({
|
|
113603
113738
|
headless: true,
|
|
@@ -113644,6 +113779,9 @@ async function validateInBrowser(projectDir, opts) {
|
|
|
113644
113779
|
});
|
|
113645
113780
|
await page.goto(server.url, { waitUntil: "domcontentloaded", timeout: 1e4 });
|
|
113646
113781
|
await new Promise((r2) => setTimeout(r2, opts.timeout ?? 3e3));
|
|
113782
|
+
for (const w3 of await auditClipDurations(page, analyzeClipMediaFit2)) {
|
|
113783
|
+
warnings.push(w3);
|
|
113784
|
+
}
|
|
113647
113785
|
if (opts.contrast) {
|
|
113648
113786
|
contrast = await runContrastAudit(page);
|
|
113649
113787
|
}
|