aboutsummaryrefslogtreecommitdiff
path: root/ext/bg/js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/bg/js')
-rw-r--r--ext/bg/js/audio-downloader.js62
-rw-r--r--ext/bg/js/backend.js3
-rw-r--r--ext/bg/js/options.js1
3 files changed, 63 insertions, 3 deletions
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;
+ }
}
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js
index 2949cbed..60739aab 100644
--- a/ext/bg/js/backend.js
+++ b/ext/bg/js/backend.js
@@ -1581,7 +1581,7 @@ class Backend {
throw new Error('Invalid reading and expression');
}
- const {sources, customSourceUrl} = details;
+ const {sources, customSourceUrl, customSourceType} = details;
const data = await this._downloadDefinitionAudio(
sources,
expression,
@@ -1589,6 +1589,7 @@ class Backend {
{
textToSpeechVoice: null,
customSourceUrl,
+ customSourceType,
binary: true,
disableCache: true
}
diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js
index 1690efb0..10919ae3 100644
--- a/ext/bg/js/options.js
+++ b/ext/bg/js/options.js
@@ -726,6 +726,7 @@ class OptionsUtil {
windowType: 'popup',
windowState: 'normal'
};
+ profile.options.audio.customSourceType = 'audio';
}
return options;
}