diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2022-08-20 11:17:24 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-20 11:17:24 -0400 |
commit | 310303ca1a123a77f9bd116af4dc64ad9c3256c5 (patch) | |
tree | af8bad0ec544625970a5f2a4613fff27773b162c /ext/js/background/request-builder.js | |
parent | 02483a45b1b7fb0654b3f37571b92400b76734a5 (diff) |
Audio download timeout (#2187)
* Add support for an idle timeout when downloading audio
* Update eslint rules
* Pass idleTimeout to the downloader from DisplayAnki
* Add anki.downloadTimeout setting
* Update tests
* Assign _audioDownloadIdleTimeout using settings
* Show info about cancelled downloads
* Handle Firefox bug
* Improve audio errors
* Refactor
* Move functions to RequestBuilder
Diffstat (limited to 'ext/js/background/request-builder.js')
-rw-r--r-- | ext/js/background/request-builder.js | 90 |
1 files changed, 89 insertions, 1 deletions
diff --git a/ext/js/background/request-builder.js b/ext/js/background/request-builder.js index ad1536f1..2cdd6f0e 100644 --- a/ext/js/background/request-builder.js +++ b/ext/js/background/request-builder.js @@ -42,6 +42,58 @@ class RequestBuilder { return await this._fetchInternal(url, init, headerModifications); } + static async readFetchResponseArrayBuffer(response, onProgress) { + let reader; + try { + if (typeof onProgress === 'function') { + reader = response.body.getReader(); + } + } catch (e) { + // Not supported + } + + if (typeof reader === 'undefined') { + const result = await response.arrayBuffer(); + if (typeof onProgress === 'function') { + onProgress(true); + } + return result; + } + + const contentLengthString = response.headers.get('Content-Length'); + const contentLength = contentLengthString !== null ? Number.parseInt(contentLengthString, 10) : null; + let target = Number.isFinite(contentLength) ? new Uint8Array(contentLength) : null; + let targetPosition = 0; + let totalLength = 0; + const targets = []; + + while (true) { + const {done, value} = await reader.read(); + if (done) { break; } + onProgress(false); + if (target === null) { + targets.push({array: value, length: value.length}); + } else if (targetPosition + value.length > target.length) { + targets.push({array: target, length: targetPosition}); + target = null; + } else { + target.set(value, targetPosition); + targetPosition += value.length; + } + totalLength += value.length; + } + + if (target === null) { + target = this._joinUint8Arrays(targets, totalLength); + } else if (totalLength < target.length) { + target = target.slice(0, totalLength); + } + + onProgress(true); + + return target; + } + // Private async _fetchInternal(url, init, headerModifications) { @@ -92,7 +144,10 @@ class RequestBuilder { }, 100); } const details = await errorDetailsPromise; - e.data = {details}; + if (details !== null) { + const data = {details}; + this._assignErrorData(e, data); + } throw e; } finally { this._removeWebRequestEventListeners(eventListeners); @@ -295,4 +350,37 @@ class RequestBuilder { } return result; } + + _assignErrorData(error, data) { + try { + error.data = data; + } catch (e) { + // On Firefox, assigning DOMException.data can fail in certain contexts. + // https://bugzilla.mozilla.org/show_bug.cgi?id=1776555 + try { + Object.defineProperty(error, 'data', { + configurable: true, + enumerable: true, + writable: true, + value: data + }); + } catch (e2) { + // NOP + } + } + } + + static _joinUint8Arrays(items, totalLength) { + if (items.length === 1) { + const {array, length} = items[0]; + if (array.length === length) { return array; } + } + const result = new Uint8Array(totalLength); + let position = 0; + for (const {array, length} of items) { + result.set(array, position); + position += length; + } + return result; + } } |