From cd64239ba17c44a8060674b65da36e976eac4ae7 Mon Sep 17 00:00:00 2001 From: Daniel <790119+DanTheMan827@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:50:29 -0500 Subject: [PATCH] refactor(downloads): add `readableStreamIterator` for easier stream handling - Introduced `readableStreamIterator` to convert ReadableStream into async iterable. - Updated `LosslessAPI` to utilize `readableStreamIterator` for handling response body. - Modified `ZipNeutralinoWriter` to use `readableStreamIterator` for reading chunks. --- js/api.js | 15 +++++---------- js/bulk-download-writer.ts | 4 +--- js/readableStreamIterator.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 js/readableStreamIterator.ts diff --git a/js/api.js b/js/api.js index 725b066..8dbd02e 100644 --- a/js/api.js +++ b/js/api.js @@ -21,6 +21,7 @@ import { triggerDownload, applyAudioPostProcessing } from './download-utils.ts'; import { isCustomFormat } from './ffmpegFormats.ts'; import { DownloadProgress } from './progressEvents.js'; import { resolveDownloadTotalBytes } from './downloadProgressUtils.js'; +import { readableStreamIterator } from './readableStreamIterator.js'; export const DASH_MANIFEST_UNAVAILABLE_CODE = 'DASH_MANIFEST_UNAVAILABLE'; export { resolveDownloadTotalBytes }; @@ -1431,19 +1432,13 @@ export class LosslessAPI { let receivedBytes = 0; if (response.body) { - const reader = response.body.getReader(); const chunks = []; - while (true) { - const { done, value } = await reader.read(); - if (done) break; + for await (const chunk of readableStreamIterator(response.body)) { + chunks.push(chunk); + receivedBytes += chunk.byteLength; - if (value) { - chunks.push(value); - receivedBytes += value.byteLength; - - onProgress?.(new DownloadProgress(receivedBytes, totalBytes || undefined)); - } + onProgress?.(new DownloadProgress(receivedBytes, totalBytes || undefined)); } const defaultMime = isVideo ? 'video/mp4' : 'audio/flac'; diff --git a/js/bulk-download-writer.ts b/js/bulk-download-writer.ts index 581eaa2..d0975f8 100644 --- a/js/bulk-download-writer.ts +++ b/js/bulk-download-writer.ts @@ -135,9 +135,7 @@ export class ZipNeutralinoWriter implements IBulkDownloadWriter { const reader = response.body.getReader(); let receivedLength = 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; + for await (const value of readableStreamIterator(response.body)) { const chunk = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength); await bridge.filesystem.appendBinaryFile(savePath, chunk); receivedLength += value.length; diff --git a/js/readableStreamIterator.ts b/js/readableStreamIterator.ts new file mode 100644 index 0000000..fe38802 --- /dev/null +++ b/js/readableStreamIterator.ts @@ -0,0 +1,28 @@ +/** + * Converts a ReadableStream into an async iterable iterator. + * @template T The type of data chunks yielded from the stream. + * @param stream The ReadableStream to convert into an async iterable. + * @yields Chunks of data from the stream as they become available. + * @example + * ```typescript + * const response = await fetch('https://example.com/data'); + * for await (const chunk of readableStreamIterator(response.body)) { + * console.log(chunk); + * } + * ``` + */ +export async function* readableStreamIterator(stream: ReadableStream): AsyncIterableIterator { + const reader = stream.getReader(); + + while (true) { + const { value, done } = await reader.read(); + + if (value) { + yield value; + } + + if (done) { + break; + } + } +}