From ebfef0c74858607d050da2cdc9046a681b133b25 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 23 Jan 2021 22:46:00 -0500 Subject: Multiple custom audio sources (#1303) * Fix label * Fix icon size being flexible * Add schema * Add customSourceType option * Update settings * Pass customSourceType to the audio downloader * Implement custom audio JSON mode --- ext/bg/js/audio-downloader.js | 62 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) (limited to 'ext/bg/js/audio-downloader.js') diff --git a/ext/bg/js/audio-downloader.js b/ext/bg/js/audio-downloader.js index 495b6399..62abda8f 100644 --- a/ext/bg/js/audio-downloader.js +++ b/ext/bg/js/audio-downloader.js @@ -16,6 +16,7 @@ */ /* global + * JsonSchemaValidator * NativeSimpleDOMParser * SimpleDOMParser */ @@ -24,6 +25,8 @@ class AudioDownloader { constructor({japaneseUtil, requestBuilder}) { this._japaneseUtil = japaneseUtil; this._requestBuilder = requestBuilder; + this._customAudioListSchema = null; + this._schemaValidator = null; this._getInfoHandlers = new Map([ ['jpod101', this._getInfoJpod101.bind(this)], ['jpod101-alternate', this._getInfoJpod101Alternate.bind(this)], @@ -183,13 +186,50 @@ class AudioDownloader { return [{type: 'tts', text: reading || expression, voice: textToSpeechVoice}]; } - async _getInfoCustom(expression, reading, {customSourceUrl}) { + async _getInfoCustom(expression, reading, {customSourceUrl, customSourceType}) { if (typeof customSourceUrl !== 'string') { throw new Error('No custom URL defined'); } const data = {expression, reading}; const url = customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (Object.prototype.hasOwnProperty.call(data, m1) ? `${data[m1]}` : m0)); - return [{type: 'url', url}]; + + switch (customSourceType) { + case 'json': + return await this._getInfoCustomJson(url); + default: + return [{type: 'url', url}]; + } + } + + async _getInfoCustomJson(url) { + const response = await this._requestBuilder.fetchAnonymous(url, { + method: 'GET', + mode: 'cors', + cache: 'default', + credentials: 'omit', + redirect: 'follow', + referrerPolicy: 'no-referrer' + }); + + if (!response.ok) { + throw new Error(`Invalid response: ${response.status}`); + } + + const responseJson = await response.json(); + + const schema = await this._getCustomAudioListSchema(); + if (this._schemaValidator === null) { + this._schemaValidator = new JsonSchemaValidator(); + } + this._schemaValidator.validate(responseJson, schema); + + const results = []; + for (const {url: url2, name} of responseJson.audioSources) { + const info = {type: 'url', url: url2}; + if (typeof name === 'string') { info.name = name; } + results.push(info); + } + return results; } async _downloadAudioFromUrl(url, source) { @@ -254,4 +294,22 @@ class AudioDownloader { throw new Error('DOM parsing not supported'); } } + + async _getCustomAudioListSchema() { + let schema = this._customAudioListSchema; + if (schema === null) { + const url = chrome.runtime.getURL('/bg/data/custom-audio-list-schema.json'); + const response = await fetch(url, { + method: 'GET', + mode: 'no-cors', + cache: 'default', + credentials: 'omit', + redirect: 'follow', + referrerPolicy: 'no-referrer' + }); + schema = await response.json(); + this._customAudioListSchema = schema; + } + return schema; + } } -- cgit v1.2.3