diff --git a/services/web-ui/public/screens-asset.jsx b/services/web-ui/public/screens-asset.jsx index 5ebe270..65d0966 100644 --- a/services/web-ui/public/screens-asset.jsx +++ b/services/web-ui/public/screens-asset.jsx @@ -88,12 +88,19 @@ function AssetDetail({ asset, onClose }) { let cancelled = false; const build = async function() { setFilmstripLoading(true); + const probe = document.createElement('video'); + probe.crossOrigin = 'anonymous'; + probe.muted = true; + probe.playsInline = true; + probe.preload = 'auto'; + probe.style.cssText = 'position:fixed;left:-9999px;top:-9999px;width:1px;height:1px;pointer-events:none'; + document.body.appendChild(probe); + const timeout = setTimeout(function() { + probe.remove(); + if (!cancelled) { setFilmFrames([]); setFilmstripLoading(false); } + cancelled = true; + }, 15000); try { - const probe = document.createElement('video'); - probe.crossOrigin = 'anonymous'; - probe.muted = true; - probe.playsInline = true; - probe.preload = 'auto'; if (streamType === 'hls') { if (!window.Hls) throw new Error('hls.js not loaded'); await new Promise(function(resolve, reject) { @@ -107,10 +114,10 @@ function AssetDetail({ asset, onClose }) { hls.attachMedia(probe); }); } else { - probe.src = streamUrl; await new Promise(function(resolve, reject) { probe.onloadedmetadata = resolve; probe.onerror = reject; + probe.src = streamUrl; }); } const frameCount = 28; @@ -146,6 +153,8 @@ function AssetDetail({ asset, onClose }) { } catch (_) { if (!cancelled) setFilmFrames([]); } finally { + clearTimeout(timeout); + probe.remove(); if (!cancelled) setFilmstripLoading(false); } };