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 img = new Image();
const cleanup = () => {
img.onload = null;
img.onerror = null;
img.src = "";
};
img.onload = () => { img.onload = () => {
resolve({ width: img.naturalWidth, height: img.naturalHeight }); const dims = { width: img.naturalWidth, height: img.naturalHeight };
cleanup();
resolve(dims);
}; };
img.onerror = () => { img.onerror = () => {
cleanup();
resolve(null); resolve(null);
}; };
img.src = base64DataUrl; img.src = base64DataUrl;
@ -41,12 +49,33 @@ export function getVideoDimensions(
return; return;
} }
let resolved = false;
const video = document.createElement("video"); 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 = () => { video.onloadedmetadata = () => {
resolve({ width: video.videoWidth, height: video.videoHeight }); clearTimeout(timeout);
safeResolve({ width: video.videoWidth, height: video.videoHeight });
}; };
video.onerror = () => { video.onerror = () => {
resolve(null); clearTimeout(timeout);
safeResolve(null);
}; };
video.src = videoUrl; video.src = videoUrl;
}); });

Loading…
Cancel
Save