diff --git a/packages/cli/src/commands/snapshot.ts b/packages/cli/src/commands/snapshot.ts index f1825e044..ee6bfcfda 100644 --- a/packages/cli/src/commands/snapshot.ts +++ b/packages/cli/src/commands/snapshot.ts @@ -361,6 +361,7 @@ async function captureSnapshots( // — FFmpeg reads http(s) input directly, and Chrome-headless can't seek // it either, so without this those videos render blank in snapshots. let ffmpegInput: string | null = null; + let inputIsLocal = false; try { const url = new URL(v.src); const decodedPath = decodeURIComponent(url.pathname).replace(/^\//, ""); @@ -368,6 +369,7 @@ async function captureSnapshots( const rel = relative(projectDir, candidate); if (!rel.startsWith("..") && !isAbsolute(rel) && existsSync(candidate)) { ffmpegInput = candidate; + inputIsLocal = true; } else if (url.protocol === "http:" || url.protocol === "https:") { ffmpegInput = url.href; } @@ -375,10 +377,19 @@ async function captureSnapshots( /* unresolvable src (e.g. blob:, data:) — skip */ } if (!ffmpegInput) continue; + // VP9-alpha detection shells out to ffprobe, which has no timeout. + // Only probe local files (filesystem-bounded); for remote URLs skip it + // (pass false) so a stalled host can't wedge snapshot in ffprobe before + // the bounded extractVideoFrameToBuffer below ever runs. Remote + // VP9-alpha overlays aren't a current path — revisit with a bounded + // ffprobe if one appears. + const useVp9AlphaDecoder = inputIsLocal + ? await shouldUseVp9AlphaDecoder(ffmpegInput) + : false; const png = await extractVideoFrameToBuffer( ffmpegInput, Math.max(0, v.relTime), - await shouldUseVp9AlphaDecoder(ffmpegInput), + useVp9AlphaDecoder, ); if (!png) continue; updates.push({