From ea00b52389c5804ecfe2fd84b9328d44c82c587f Mon Sep 17 00:00:00 2001 From: miga-heygen Date: Mon, 31 Aug 2026 22:22:56 +0000 Subject: [PATCH 1/2] fix(engine): flag single-keyframe videos as problematic A video with exactly one keyframe is the worst case for the sparse- keyframe warning: every seek past 0 lands inside a single GOP that spans the entire file. The previous early return treated < 2 keyframes as not-problematic, silently skipping a 10-second single-GOP video while warning about a 5-second two-keyframe one. Split the early return: 0 keyframes (still image / no video stream) remains not-problematic; 1 keyframe is now flagged. Fixes #3460. --- packages/engine/src/utils/ffprobe.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/engine/src/utils/ffprobe.ts b/packages/engine/src/utils/ffprobe.ts index c4358494e1..e13fbc8324 100644 --- a/packages/engine/src/utils/ffprobe.ts +++ b/packages/engine/src/utils/ffprobe.ts @@ -1050,14 +1050,22 @@ async function analyzeKeyframeIntervalsUncached(filePath: string): Promise parseFloat(line.trim())) .filter((t) => Number.isFinite(t)); - if (timestamps.length < 2) { + if (timestamps.length === 0) { return { avgIntervalSeconds: 0, maxIntervalSeconds: 0, - keyframeCount: timestamps.length, + keyframeCount: 0, isProblematic: false, }; } + if (timestamps.length === 1) { + return { + avgIntervalSeconds: 0, + maxIntervalSeconds: 0, + keyframeCount: 1, + isProblematic: true, + }; + } let maxInterval = 0; let totalInterval = 0; From 48ca93fd3d62d5761e372598a445d157b525a13f Mon Sep 17 00:00:00 2001 From: miga-heygen Date: Mon, 31 Aug 2026 22:24:33 +0000 Subject: [PATCH 2/2] fix(producer): degrade gracefully when font cache directory is unwritable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the font cache root (~/.cache/hyperframes/fonts/) cannot be created (EPERM on read-only filesystems, restricted home directories, etc.), the render aborts with a raw mkdir error. The cache is an optimization, not a requirement — a missing cache should mean slower first renders, not broken renders. Fall back to a temporary directory under os.tmpdir() when the configured cache root fails, so Google Fonts downloads still proceed. The fallback cache is per-process and not persistent across renders, but the render completes. Fixes #3412. --- packages/producer/src/services/deterministicFonts.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/producer/src/services/deterministicFonts.ts b/packages/producer/src/services/deterministicFonts.ts index 7b99aad6e1..8c41c886fe 100644 --- a/packages/producer/src/services/deterministicFonts.ts +++ b/packages/producer/src/services/deterministicFonts.ts @@ -749,7 +749,13 @@ function fontSlug(familyName: string): string { function fontCacheDir(slug: string): string { const dir = join(resolveFontCacheRoot(), slug); if (!existsSync(dir)) { - mkdirSync(dir, { recursive: true }); + try { + mkdirSync(dir, { recursive: true }); + } catch { + const fallback = join(tmpdir(), `hyperframes-fonts-fallback`, slug); + mkdirSync(fallback, { recursive: true }); + return fallback; + } } return dir; }