From 321fd63f5a2be06fe8da3af1c9a994fb19921592 Mon Sep 17 00:00:00 2001 From: Ender Date: Mon, 27 Oct 2025 20:21:27 +0100 Subject: [PATCH] feat: improve audio clip URL handling after upload - Added URL switching from blob to server URL after successful clip upload - Implemented memory cleanup by revoking blob URLs when no longer needed - Updated clip state to include server-generated URL using bucket and key data - Improved error handling to preserve existing URL if server doesn't return storage data --- apps/admin/src/features/recorder/Recorder.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/apps/admin/src/features/recorder/Recorder.tsx b/apps/admin/src/features/recorder/Recorder.tsx index 9b6f959..5b83d09 100644 --- a/apps/admin/src/features/recorder/Recorder.tsx +++ b/apps/admin/src/features/recorder/Recorder.tsx @@ -82,7 +82,27 @@ export default function Recorder({ postId, initialClips, onInsertAtCursor, onTra throw new Error(`Upload failed: ${res.status} ${txt}`); } const data = await res.json(); - setClips((prev) => prev.map(x => x.id === id ? { ...x, id: (data.clipId || x.id), uploadedKey: data.key || 'uploaded', uploadedBucket: data.bucket || null, isUploading: false } : x)); + // Update clip with server data and switch to server URL + const serverUrl = data.bucket && data.key + ? `/api/media/obj?bucket=${encodeURIComponent(data.bucket)}&key=${encodeURIComponent(data.key)}` + : undefined; + setClips((prev) => prev.map(x => { + if (x.id === id) { + // Revoke old blob URL to free memory + if (x.url && x.url.startsWith('blob:')) { + URL.revokeObjectURL(x.url); + } + return { + ...x, + id: (data.clipId || x.id), + uploadedKey: data.key || 'uploaded', + uploadedBucket: data.bucket || null, + url: serverUrl || x.url, // Use server URL after upload + isUploading: false + }; + } + return x; + })); } catch (e: any) { setClips((prev) => prev.map(x => x.id === id ? { ...x, error: e?.message || 'Upload failed', isUploading: false } : x)); }