diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-09-03 22:33:58 -0400 |
---|---|---|
committer | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2021-09-11 16:05:39 -0400 |
commit | ee2466eb22754c7f4a3296d23f8002bb97c4dfb9 (patch) | |
tree | 535801cfabec21a81d2a9ee57b14ef1b8f7678ed /ext/js | |
parent | 764d59df137dacfa6b4cfa8394b711fda904efd9 (diff) |
Dictionary media import improvements (#1926)
* Add base64ToArrayBuffer to StringUtil
* Remove unnecessary media-util.js import
* Run async requirements in serial rather than parallel
* Update API.getMedia handler to convert ArrayBuffer content to base64
* Rename getImageResolution to getImageDetails
* Change parameter order of getImageDetails
* Pre-process and store media as an ArrayBuffer
* Remove MediaUtil.createBlobFromBase64Content
* Fix Anki media injection
Diffstat (limited to 'ext/js')
-rw-r--r-- | ext/js/background/backend.js | 16 | ||||
-rw-r--r-- | ext/js/data/sandbox/string-util.js | 15 | ||||
-rw-r--r-- | ext/js/language/dictionary-importer-media-loader.js | 19 | ||||
-rw-r--r-- | ext/js/language/dictionary-importer.js | 9 | ||||
-rw-r--r-- | ext/js/language/dictionary-worker-handler.js | 2 | ||||
-rw-r--r-- | ext/js/language/dictionary-worker-media-loader.js | 16 | ||||
-rw-r--r-- | ext/js/language/dictionary-worker.js | 13 | ||||
-rw-r--r-- | ext/js/media/media-loader.js | 5 | ||||
-rw-r--r-- | ext/js/media/media-util.js | 16 |
9 files changed, 57 insertions, 54 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index dea67091..8e05ee68 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -31,6 +31,7 @@ * PermissionsUtil * ProfileConditionsUtil * RequestBuilder + * StringUtil * Translator * wanakana */ @@ -621,7 +622,7 @@ class Backend { } async _onApiGetMedia({targets}) { - return await this._dictionaryDatabase.getMedia(targets); + return await this._getNormalizedDictionaryDatabaseMedia(targets); } _onApiLog({error, level, context}) { @@ -1847,7 +1848,7 @@ class Backend { detailsList.push(details); detailsMap.set(key, details); } - const mediaList = await this._dictionaryDatabase.getMedia(targets); + const mediaList = await this._getNormalizedDictionaryDatabaseMedia(targets); for (const media of mediaList) { const {dictionary, path} = media; @@ -2283,4 +2284,15 @@ class Backend { return await this._injectScript(file, tab.id, frameId); } + + async _getNormalizedDictionaryDatabaseMedia(targets) { + const results = await this._dictionaryDatabase.getMedia(targets); + for (const item of results) { + const {content} = item; + if (content instanceof ArrayBuffer) { + item.content = StringUtil.arrayBufferToBase64(content); + } + } + return results; + } } diff --git a/ext/js/data/sandbox/string-util.js b/ext/js/data/sandbox/string-util.js index 37e021c9..b523c39d 100644 --- a/ext/js/data/sandbox/string-util.js +++ b/ext/js/data/sandbox/string-util.js @@ -58,4 +58,19 @@ class StringUtil { return binary; } } + + /** + * Converts a base64 string to an ArrayBuffer. + * @param content The binary content string encoded in base64. + * @returns A new `ArrayBuffer` object corresponding to the specified content. + */ + static base64ToArrayBuffer(content) { + const binaryContent = atob(content); + const length = binaryContent.length; + const array = new Uint8Array(length); + for (let i = 0; i < length; ++i) { + array[i] = binaryContent.charCodeAt(i); + } + return array.buffer; + } } diff --git a/ext/js/language/dictionary-importer-media-loader.js b/ext/js/language/dictionary-importer-media-loader.js index bbcc5476..27ddde34 100644 --- a/ext/js/language/dictionary-importer-media-loader.js +++ b/ext/js/language/dictionary-importer-media-loader.js @@ -15,23 +15,17 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/* global - * MediaUtil - */ - /** * Class used for loading and validating media during the dictionary import process. */ class DictionaryImporterMediaLoader { /** - * Attempts to load an image using a base64 encoded content and a media type - * and returns its resolution. + * Attempts to load an image using an ArrayBuffer and a media type to return details about it. + * @param content The binary content for the image, encoded as an ArrayBuffer. * @param mediaType The media type for the image content. - * @param content The binary content for the image, encoded in base64. - * @returns A Promise which resolves with {width, height} on success, - * otherwise an error is thrown. + * @returns A Promise which resolves with {content, width, height} on success, otherwise an error is thrown. */ - getImageResolution(mediaType, content) { + getImageDetails(content, mediaType, transfer) { return new Promise((resolve, reject) => { const image = new Image(); const eventListeners = new EventListenerCollection(); @@ -42,14 +36,15 @@ class DictionaryImporterMediaLoader { }; eventListeners.addEventListener(image, 'load', () => { const {naturalWidth: width, naturalHeight: height} = image; + if (Array.isArray(transfer)) { transfer.push(content); } cleanup(); - resolve({width, height}); + resolve({content, width, height}); }, false); eventListeners.addEventListener(image, 'error', () => { cleanup(); reject(new Error('Image failed to load')); }, false); - const blob = MediaUtil.createBlobFromBase64Content(content, mediaType); + const blob = new Blob([content], {type: mediaType}); const url = URL.createObjectURL(blob); image.src = url; }); diff --git a/ext/js/language/dictionary-importer.js b/ext/js/language/dictionary-importer.js index 98cddf76..5629e197 100644 --- a/ext/js/language/dictionary-importer.js +++ b/ext/js/language/dictionary-importer.js @@ -328,13 +328,10 @@ class DictionaryImporter { const media = new Map(); const context = {archive, media}; - const promises = []; for (const requirement of requirements) { - promises.push(this._resolveAsyncRequirement(context, requirement)); + await this._resolveAsyncRequirement(context, requirement); } - await Promise.all(promises); - return { media: [...media.values()] }; @@ -425,7 +422,7 @@ class DictionaryImporter { } // Load file content - const content = await file.async('base64'); + let content = await file.async('arraybuffer'); const mediaType = MediaUtil.getImageMediaTypeFromFileName(path); if (mediaType === null) { throw createError('Could not determine media type for image'); @@ -435,7 +432,7 @@ class DictionaryImporter { let width; let height; try { - ({width, height} = await this._mediaLoader.getImageResolution(mediaType, content)); + ({content, width, height} = await this._mediaLoader.getImageDetails(content, mediaType)); } catch (e) { throw createError('Could not load image'); } diff --git a/ext/js/language/dictionary-worker-handler.js b/ext/js/language/dictionary-worker-handler.js index 1d6b4aab..3ce744f9 100644 --- a/ext/js/language/dictionary-worker-handler.js +++ b/ext/js/language/dictionary-worker-handler.js @@ -44,7 +44,7 @@ class DictionaryWorkerHandler { case 'getDictionaryCounts': this._onMessageWithProgress(params, this._getDictionaryCounts.bind(this)); break; - case 'getImageResolution.response': + case 'getImageDetails.response': this._mediaLoader.handleMessage(params); break; } diff --git a/ext/js/language/dictionary-worker-media-loader.js b/ext/js/language/dictionary-worker-media-loader.js index 90ee513f..25f8de72 100644 --- a/ext/js/language/dictionary-worker-media-loader.js +++ b/ext/js/language/dictionary-worker-media-loader.js @@ -45,21 +45,19 @@ class DictionaryWorkerMediaLoader { } /** - * Attempts to load an image using a base64 encoded content and a media type - * and returns its resolution. + * Attempts to load an image using an ArrayBuffer and a media type to return details about it. + * @param content The binary content for the image, encoded as an ArrayBuffer. * @param mediaType The media type for the image content. - * @param content The binary content for the image, encoded in base64. - * @returns A Promise which resolves with {width, height} on success, - * otherwise an error is thrown. + * @returns A Promise which resolves with {content, width, height} on success, otherwise an error is thrown. */ - getImageResolution(mediaType, content) { + getImageDetails(content, mediaType) { return new Promise((resolve, reject) => { const id = generateId(16); this._requests.set(id, {resolve, reject}); self.postMessage({ - action: 'getImageResolution', - params: {id, mediaType, content} - }); + action: 'getImageDetails', + params: {id, content, mediaType} + }, [content]); }); } } diff --git a/ext/js/language/dictionary-worker.js b/ext/js/language/dictionary-worker.js index 92faa3dc..be94c397 100644 --- a/ext/js/language/dictionary-worker.js +++ b/ext/js/language/dictionary-worker.js @@ -85,8 +85,8 @@ class DictionaryWorker { case 'progress': this._onMessageProgress(params, details.onProgress); break; - case 'getImageResolution': - this._onMessageGetImageResolution(params, details.worker); + case 'getImageDetails': + this._onMessageGetImageDetails(params, details.worker); break; } } @@ -115,16 +115,17 @@ class DictionaryWorker { onProgress(...args); } - async _onMessageGetImageResolution(params, worker) { - const {id, mediaType, content} = params; + async _onMessageGetImageDetails(params, worker) { + const {id, content, mediaType} = params; + const transfer = []; let response; try { - const result = await this._dictionaryImporterMediaLoader.getImageResolution(mediaType, content); + const result = await this._dictionaryImporterMediaLoader.getImageDetails(content, mediaType, transfer); response = {id, result}; } catch (e) { response = {id, error: serializeError(e)}; } - worker.postMessage({action: 'getImageResolution.response', params: response}); + worker.postMessage({action: 'getImageDetails.response', params: response}, transfer); } _formatimportDictionaryResult(result) { diff --git a/ext/js/media/media-loader.js b/ext/js/media/media-loader.js index 7dafc2a3..8c7e356c 100644 --- a/ext/js/media/media-loader.js +++ b/ext/js/media/media-loader.js @@ -16,7 +16,7 @@ */ /* global - * MediaUtil + * StringUtil */ class MediaLoader { @@ -86,7 +86,8 @@ class MediaLoader { const token = this._token; const data = (await yomichan.api.getMedia([{path, dictionary}]))[0]; if (token === this._token && data !== null) { - const blob = MediaUtil.createBlobFromBase64Content(data.content, data.mediaType); + const buffer = StringUtil.base64ToArrayBuffer(data.content); + const blob = new Blob([buffer], {type: data.mediaType}); const url = URL.createObjectURL(blob); cachedData.data = data; cachedData.url = url; diff --git a/ext/js/media/media-util.js b/ext/js/media/media-util.js index f783038a..11172c5c 100644 --- a/ext/js/media/media-util.js +++ b/ext/js/media/media-util.js @@ -129,20 +129,4 @@ class MediaUtil { return null; } } - - /** - * Creates a new `Blob` object from a base64 string of content. - * @param content The binary content string encoded in base64. - * @param mediaType The type of the media. - * @returns A new `Blob` object corresponding to the specified content. - */ - static createBlobFromBase64Content(content, mediaType) { - const binaryContent = atob(content); - const length = binaryContent.length; - const array = new Uint8Array(length); - for (let i = 0; i < length; ++i) { - array[i] = binaryContent.charCodeAt(i); - } - return new Blob([array.buffer], {type: mediaType}); - } } |