From 9e1e25ac9709ca9fdbe91aaf1acfd20278022b40 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Mon, 6 Apr 2026 21:56:03 -0400 Subject: [PATCH] fix: web GUI upload Content-Type mismatch with presigned URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same fix as the Chrome extension — the web GUI's uploadHTTP() was using item.file.type (browser-determined MIME) instead of presigned.contentType (server-signed MIME). For broadcast formats like MXF, R3D, BRAW where the browser returns empty or generic types, this causes an S3 signature mismatch and the PUT fails. Co-Authored-By: Claude Sonnet 4.6 --- public/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/index.html b/public/index.html index ac236f5..6609ff6 100644 --- a/public/index.html +++ b/public/index.html @@ -1083,12 +1083,14 @@ async function uploadHTTP(files) { try { const presigned = await api('POST','/api/presigned',{filename:item.name,prefix:selectedPrefix,contentType:item.file.type||'application/octet-stream'}); if (!presigned.success) throw new Error(presigned.error||'Failed to get presigned URL'); + // Use the content type the server signed — browser file.type may differ for broadcast formats + const signedType = presigned.contentType || item.file.type || 'application/octet-stream'; await new Promise((resolve,reject) => { const xhr=new XMLHttpRequest(); xhr.open('PUT',presigned.url); - xhr.setRequestHeader('Content-Type',item.file.type||'application/octet-stream'); + xhr.setRequestHeader('Content-Type',signedType); xhr.upload.onprogress=e=>{ if(e.lengthComputable){ const p=Math.round(e.loaded/e.total*100); document.getElementById(`progbar-${idx}`).style.width=p+'%'; setFileStatus(idx,'uploading',p+'%'); } }; - xhr.onload=()=>xhr.status<300?resolve():reject(new Error(`S3 ${xhr.status}`)); + xhr.onload=()=>xhr.status<300?resolve():reject(new Error(`S3 error ${xhr.status}`)); xhr.onerror=()=>reject(new Error('Network error')); xhr.send(item.file); });