diff --git a/static/audio-store.js b/static/audio-store.js index c2567ca..5fabd08 100644 --- a/static/audio-store.js +++ b/static/audio-store.js @@ -165,41 +165,23 @@ export async function sweepIncomplete() { } /** - * Stream stored audio to an endpoint as a raw body. + * Send stored audio to an endpoint as a raw body. * - * The body is sent as a stream (duplex: 'half') so a 200 MB file doesn't have to - * be materialised as a second in-memory copy just to be uploaded. + * The OPFS File is passed to fetch as-is: the browser reads a Blob body lazily + * from its backing store, so a 200 MB master is never materialised in memory. + * (A ReadableStream body with duplex: 'half' was used before, but Chrome only + * allows streaming uploads over HTTP/2+, so any file past the old 8 MB cutoff + * failed with "Failed to fetch" on HTTP/1.1 — e.g. http://localhost.) */ export async function send(fileId, url, {method = 'POST', onProgress} = {}) { const blob = await get(fileId); if (!blob) throw new Error('audio is no longer in browser storage — re-add the file'); - // Small files go as a plain body: some proxies mishandle streaming duplex - // requests, and below a few MB there is nothing to gain. - if (blob.size < 8 * 1024 * 1024) { - const buf = await blob.arrayBuffer(); - if (onProgress) onProgress(blob.size, blob.size); - return fetch(url, { - method, - headers: {'Content-Type': 'application/octet-stream'}, - body: buf, - }); - } - - let sent = 0; - const body = blob.stream().pipeThrough( - new TransformStream({ - transform(chunk, controller) { - sent += chunk.byteLength; - if (onProgress) onProgress(sent, blob.size); - controller.enqueue(chunk); - }, - }), - ); - return fetch(url, { + const res = await fetch(url, { method, headers: {'Content-Type': 'application/octet-stream'}, - body, - duplex: 'half', + body: blob, }); + if (onProgress) onProgress(blob.size, blob.size); + return res; }