mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 00:56:23 +00:00
fix: double-audio bug + lint rules + docs guide + capture improvements
Double-audio bug fix: - scaffolding.ts: stop writing index.html in captures/ (root cause — runtime discovered scaffold + real index.html as two compositions) - New lint rule: multiple_root_compositions — errors if >1 root HTML - New lint rule: duplicate_audio_track — warns on overlapping audio Capture improvements (from testing 30+ websites): - Catalog runs BEFORE extractHtml (which mutates DOM — converts img src to data URLs). HeyKuba: 2 images → 78. - networkidle2 instead of networkidle0 (unblocks SPAs with WebSockets) - Lazy-load image wait, CSS background-image cataloging - SVG naming from class/id/parent (not just aria-label) - Gemini batch 5→20, pause 12s→2s, maxOutputTokens 300→500 - Asset descriptions sorted: captioned first Docs: - New guide: guides/website-to-video.mdx (full tutorial) - CLI docs: added capture and snapshot commands - docs.json: website-to-video in Guides nav C
This commit is contained in:
@@ -53,6 +53,8 @@ export function lintProject(project: ProjectDir): ProjectLintResult {
|
||||
const projectFindings = [
|
||||
...lintProjectAudioFiles(project.dir, allHtmlSources),
|
||||
...lintAudioSrcNotFound(project.dir, allHtmlSources),
|
||||
...lintMultipleRootCompositions(results),
|
||||
...lintDuplicateAudioTracks(allHtmlSources),
|
||||
];
|
||||
if (projectFindings.length > 0) {
|
||||
// Append project-level findings to the root index.html result
|
||||
@@ -154,6 +156,68 @@ function lintAudioSrcNotFound(projectDir: string, htmlSources: string[]): Hyperf
|
||||
return findings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error if multiple root-level HTML files exist (not in compositions/).
|
||||
* Catches the double-audio bug where a scaffold and the real index.html
|
||||
* both register as root compositions.
|
||||
*/
|
||||
function lintMultipleRootCompositions(
|
||||
results: Array<{ file: string; result: HyperframeLintResult }>,
|
||||
): HyperframeLintFinding[] {
|
||||
const findings: HyperframeLintFinding[] = [];
|
||||
const rootFiles = results.map((r) => r.file).filter((f) => !f.startsWith("compositions/"));
|
||||
|
||||
if (rootFiles.length > 1) {
|
||||
findings.push({
|
||||
code: "multiple_root_compositions",
|
||||
severity: "error",
|
||||
message: `Multiple root-level HTML files found: ${rootFiles.join(", ")}. The runtime may discover both as composition entry points, causing duplicate audio playback.`,
|
||||
fixHint:
|
||||
"A project should have exactly one root index.html. Remove or rename extra root-level HTML files.",
|
||||
});
|
||||
}
|
||||
return findings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if multiple <audio> elements on the same data-track-index overlap in time.
|
||||
* This causes layered audio playback.
|
||||
*/
|
||||
function lintDuplicateAudioTracks(htmlSources: string[]): HyperframeLintFinding[] {
|
||||
const findings: HyperframeLintFinding[] = [];
|
||||
const audioRe =
|
||||
/<audio\b[^>]*\bdata-track-index\s*=\s*["'](\d+)["'][^>]*\bdata-start\s*=\s*["']([^"']+)["'][^>]*\bdata-duration\s*=\s*["']([^"']+)["'][^>]*>/gi;
|
||||
|
||||
const tracks: Array<{ trackIndex: number; start: number; end: number; src: string }> = [];
|
||||
for (const html of htmlSources) {
|
||||
let match: RegExpExecArray | null;
|
||||
while ((match = audioRe.exec(html)) !== null) {
|
||||
const trackIndex = parseInt(match[1]!, 10);
|
||||
const start = parseFloat(match[2]!);
|
||||
const duration = parseFloat(match[3]!);
|
||||
const srcMatch = match[0].match(/\bsrc\s*=\s*["']([^"']+)["']/);
|
||||
tracks.push({ trackIndex, start, end: start + duration, src: srcMatch?.[1] ?? "unknown" });
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < tracks.length; i++) {
|
||||
for (let j = i + 1; j < tracks.length; j++) {
|
||||
const a = tracks[i]!;
|
||||
const b = tracks[j]!;
|
||||
if (a.trackIndex !== b.trackIndex) continue;
|
||||
if (a.start < b.end && b.start < a.end) {
|
||||
findings.push({
|
||||
code: "duplicate_audio_track",
|
||||
severity: "warning",
|
||||
message: `Multiple <audio> elements on track ${a.trackIndex} overlap (${a.src} at ${a.start}-${a.end.toFixed(1)}s, ${b.src} at ${b.start}-${b.end.toFixed(1)}s). This causes layered audio playback.`,
|
||||
fixHint: "Use non-overlapping time windows or different track indices.",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return findings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether a render should be blocked based on lint results and strict mode.
|
||||
* --strict blocks on errors; --strict-all blocks on errors or warnings.
|
||||
|
||||
Reference in New Issue
Block a user