aboutsummaryrefslogtreecommitdiff
path: root/ext/js/background/request-builder.js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2022-08-20 11:17:24 -0400
committerGitHub <noreply@github.com>2022-08-20 11:17:24 -0400
commit310303ca1a123a77f9bd116af4dc64ad9c3256c5 (patch)
treeaf8bad0ec544625970a5f2a4613fff27773b162c /ext/js/background/request-builder.js
parent02483a45b1b7fb0654b3f37571b92400b76734a5 (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.js90
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;
+ }
}