Browse Source

fix: clean up video/image decoder resources in getVideoDimensions

getVideoDimensions created a <video> element for dimension detection but
never cleaned it up, leaving a phantom decoder running alongside the
visible video. On systems with weak GPUs (e.g. Intel HD 5500), this
compounds with autoPlay loop to overwhelm Chrome's main thread.

- Add cleanup helper that nulls handlers, clears src, and calls load()
- Add video.preload = "metadata" to avoid fetching full video content
- Add 10-second timeout that cleans up and resolves null if metadata
  never loads (graceful degradation: node keeps current size)
- Apply same cleanup pattern to getImageDimensions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handoff-20260429-1057
shrimbly 5 months ago
parent
commit
1c68f260df
  1. 35
      src/utils/nodeDimensions.ts

35
src/utils/nodeDimensions.ts

@ -17,10 +17,18 @@ export function getImageDimensions(
}
const img = new Image();
const cleanup = () => {
img.onload = null;
img.onerror = null;
img.src = "";
};
img.onload = () => {
resolve({ width: img.naturalWidth, height: img.naturalHeight });
const dims = { width: img.naturalWidth, height: img.naturalHeight };
cleanup();
resolve(dims);
};
img.onerror = () => {
cleanup();
resolve(null);
};
img.src = base64DataUrl;
@ -41,12 +49,33 @@ export function getVideoDimensions(
return;
}
let resolved = false;
const video = document.createElement("video");
video.preload = "metadata";
const cleanup = () => {
video.onloadedmetadata = null;
video.onerror = null;
video.src = "";
video.load();
};
const safeResolve = (value: { width: number; height: number } | null) => {
if (resolved) return;
resolved = true;
cleanup();
resolve(value);
};
const timeout = setTimeout(() => safeResolve(null), 10_000);
video.onloadedmetadata = () => {
resolve({ width: video.videoWidth, height: video.videoHeight });
clearTimeout(timeout);
safeResolve({ width: video.videoWidth, height: video.videoHeight });
};
video.onerror = () => {
resolve(null);
clearTimeout(timeout);
safeResolve(null);
};
video.src = videoUrl;
});

Loading…
Cancel
Save