From a3b3852452e40c83005ed27ba09feadb82e33095 Mon Sep 17 00:00:00 2001 From: likiosliu Date: Tue, 24 Mar 2026 19:53:14 +0800 Subject: [PATCH] fix: add AbortController to fetch call in audio download Abort in-flight download when the component unmounts or a new download starts, preventing wasted bandwidth on navigation. Closes #919 --- surfsense_web/components/tool-ui/audio.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/surfsense_web/components/tool-ui/audio.tsx b/surfsense_web/components/tool-ui/audio.tsx index acf6054fb..0bda81bb8 100644 --- a/surfsense_web/components/tool-ui/audio.tsx +++ b/surfsense_web/components/tool-ui/audio.tsx @@ -27,6 +27,7 @@ function formatTime(seconds: number): string { export function Audio({ id, src, title, description, artwork, durationMs, className }: AudioProps) { const audioRef = useRef(null); + const downloadControllerRef = useRef(null); const [isPlaying, setIsPlaying] = useState(false); const [currentTime, setCurrentTime] = useState(0); const [duration, setDuration] = useState(durationMs ? durationMs / 1000 : 0); @@ -84,8 +85,12 @@ export function Audio({ id, src, title, description, artwork, durationMs, classN // Handle download const handleDownload = useCallback(async () => { + downloadControllerRef.current?.abort(); + const controller = new AbortController(); + downloadControllerRef.current = controller; + try { - const response = await fetch(src); + const response = await fetch(src, { signal: controller.signal }); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); @@ -96,10 +101,16 @@ export function Audio({ id, src, title, description, artwork, durationMs, classN document.body.removeChild(a); window.URL.revokeObjectURL(url); } catch (err) { + if (err instanceof DOMException && err.name === "AbortError") return; console.error("Error downloading audio:", err); } }, [src, title]); + // Abort in-flight download on unmount + useEffect(() => { + return () => downloadControllerRef.current?.abort(); + }, []); + // Set up audio event listeners useEffect(() => { const audio = audioRef.current;