From 21d194d14510abb149d22c8cbd56570cd6b62266 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Mar 2020 14:25:25 -0500 Subject: [PATCH 1/5] Make _audioInject internal to Backend --- ext/bg/js/audio.js | 44 ----------------------------------------- ext/bg/js/backend.js | 47 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 48 deletions(-) diff --git a/ext/bg/js/audio.js b/ext/bg/js/audio.js index c94121ae..361a19cc 100644 --- a/ext/bg/js/audio.js +++ b/ext/bg/js/audio.js @@ -138,47 +138,3 @@ function audioUrlNormalize(url, baseUrl, basePath) { } return url; } - -function audioBuildFilename(definition) { - if (definition.reading || definition.expression) { - let filename = 'yomichan'; - if (definition.reading) { - filename += `_${definition.reading}`; - } - if (definition.expression) { - filename += `_${definition.expression}`; - } - - return filename += '.mp3'; - } - return null; -} - -async function audioInject(definition, fields, sources, optionsContext, audioSystem) { - let usesAudio = false; - for (const fieldValue of Object.values(fields)) { - if (fieldValue.includes('{audio}')) { - usesAudio = true; - break; - } - } - - if (!usesAudio) { - return true; - } - - try { - const expressions = definition.expressions; - const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition; - - const {uri} = await audioSystem.getDefinitionAudio(audioSourceDefinition, sources, {tts: false, optionsContext}); - const filename = audioBuildFilename(audioSourceDefinition); - if (filename !== null) { - definition.audio = {url: uri, filename}; - } - - return true; - } catch (e) { - return false; - } -} diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6e5235ed..1fdc4c70 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -21,7 +21,7 @@ conditionsTestValue, profileConditionsDescriptor handlebarsRenderDynamic requestText, requestJson, optionsLoad dictConfigured, dictTermsSort, dictEnabledSet -audioGetUrl, audioInject +audioGetUrl jpConvertReading, jpDistributeFuriganaInflected, jpKatakanaToHiragana AnkiNoteBuilder, AudioSystem, Translator, AnkiConnect, AnkiNull, Mecab, BackendApiForwarder, JsonSchema, ClipboardMonitor*/ @@ -434,12 +434,11 @@ class Backend { const templates = this.defaultAnkiFieldTemplates; if (mode !== 'kanji') { - await audioInject( + await this._audioInject( definition, options.anki.terms.fields, options.audio.sources, - optionsContext, - this.audioSystem + optionsContext ); } @@ -775,6 +774,35 @@ class Backend { return await audioGetUrl(definition, source, options); } + async _audioInject(definition, fields, sources, optionsContext) { + let usesAudio = false; + for (const fieldValue of Object.values(fields)) { + if (fieldValue.includes('{audio}')) { + usesAudio = true; + break; + } + } + + if (!usesAudio) { + return true; + } + + try { + const expressions = definition.expressions; + const audioSourceDefinition = Array.isArray(expressions) ? expressions[0] : definition; + + const {uri} = await this.audioSystem.getDefinitionAudio(audioSourceDefinition, sources, {tts: false, optionsContext}); + const filename = this._createInjectedAudioFileName(audioSourceDefinition); + if (filename !== null) { + definition.audio = {url: uri, filename}; + } + + return true; + } catch (e) { + return false; + } + } + async _injectScreenshot(definition, fields, screenshot) { let usesScreenshot = false; for (const fieldValue of Object.values(fields)) { @@ -815,6 +843,17 @@ class Backend { return handlebarsRenderDynamic(template, data); } + _createInjectedAudioFileName(definition) { + const {reading, expression} = definition; + if (!reading && !expression) { return null; } + + let filename = 'yomichan'; + if (reading) { filename += `_${reading}`; } + if (expression) { filename += `_${expression}`; } + filename += '.mp3'; + return filename; + } + static _getTabUrl(tab) { return new Promise((resolve) => { chrome.tabs.sendMessage(tab.id, {action: 'getUrl'}, {frameId: 0}, (response) => { From 391f3dd29af2017b540b38e67a06242af85268ba Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Mar 2020 14:36:16 -0500 Subject: [PATCH 2/5] Update how audio URIs are built --- ext/bg/js/audio.js | 104 +++++++++++++++++++++++++------------------ ext/bg/js/backend.js | 8 ++-- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/ext/bg/js/audio.js b/ext/bg/js/audio.js index 361a19cc..80e9cb9a 100644 --- a/ext/bg/js/audio.js +++ b/ext/bg/js/audio.js @@ -18,8 +18,49 @@ /*global jpIsStringEntirelyKana*/ -const audioUrlBuilders = new Map([ - ['jpod101', async (definition) => { +class AudioUriBuilder { + constructor() { + this._getUrlHandlers = new Map([ + ['jpod101', this._getUriJpod101.bind(this)], + ['jpod101-alternate', this._getUriJpod101Alternate.bind(this)], + ['jisho', this._getUriJisho.bind(this)], + ['text-to-speech', this._getUriTextToSpeech.bind(this)], + ['text-to-speech-reading', this._getUriTextToSpeechReading.bind(this)], + ['custom', this._getUriCustom.bind(this)] + ]); + } + + normalizeUrl(url, baseUrl, basePath) { + if (url) { + if (url[0] === '/') { + if (url.length >= 2 && url[1] === '/') { + // Begins with "//" + url = baseUrl.substring(0, baseUrl.indexOf(':') + 1) + url; + } else { + // Begins with "/" + url = baseUrl + url; + } + } else if (!/^[a-z][a-z0-9\-+.]*:/i.test(url)) { + // No URI scheme => relative path + url = baseUrl + basePath + url; + } + } + return url; + } + + async getUri(mode, definition, options) { + const handler = this._getUrlHandlers.get(mode); + if (typeof handler === 'function') { + try { + return await handler(definition, options); + } catch (e) { + // NOP + } + } + return null; + } + + async _getUriJpod101(definition) { let kana = definition.reading; let kanji = definition.expression; @@ -37,8 +78,9 @@ const audioUrlBuilders = new Map([ } return `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; - }], - ['jpod101-alternate', async (definition) => { + } + + async _getUriJpod101Alternate(definition) { const response = await new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post'); @@ -54,7 +96,7 @@ const audioUrlBuilders = new Map([ const url = row.querySelector('audio>source[src]').getAttribute('src'); const reading = row.getElementsByClassName('dc-vocab_kana').item(0).textContent; if (url && reading && (!definition.reading || definition.reading === reading)) { - return audioUrlNormalize(url, 'https://www.japanesepod101.com', '/learningcenter/reference/'); + return this.normalizeUrl(url, 'https://www.japanesepod101.com', '/learningcenter/reference/'); } } catch (e) { // NOP @@ -62,8 +104,9 @@ const audioUrlBuilders = new Map([ } throw new Error('Failed to find audio URL'); - }], - ['jisho', async (definition) => { + } + + async _getUriJisho(definition) { const response = await new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('GET', `https://jisho.org/search/${definition.expression}`); @@ -78,7 +121,7 @@ const audioUrlBuilders = new Map([ if (audio !== null) { const url = audio.getElementsByTagName('source').item(0).getAttribute('src'); if (url) { - return audioUrlNormalize(url, 'https://jisho.org', '/search/'); + return this.normalizeUrl(url, 'https://jisho.org', '/search/'); } } } catch (e) { @@ -86,55 +129,28 @@ const audioUrlBuilders = new Map([ } throw new Error('Failed to find audio URL'); - }], - ['text-to-speech', async (definition, options) => { + } + + async _getUriTextToSpeech(definition, options) { const voiceURI = options.audio.textToSpeechVoice; if (!voiceURI) { throw new Error('No voice'); } return `tts:?text=${encodeURIComponent(definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; - }], - ['text-to-speech-reading', async (definition, options) => { + } + + async _getUriTextToSpeechReading(definition, options) { const voiceURI = options.audio.textToSpeechVoice; if (!voiceURI) { throw new Error('No voice'); } return `tts:?text=${encodeURIComponent(definition.reading || definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; - }], - ['custom', async (definition, options) => { + } + + async _getUriCustom(definition, options) { const customSourceUrl = options.audio.customSourceUrl; return customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (hasOwn(definition, m1) ? `${definition[m1]}` : m0)); - }] -]); - -async function audioGetUrl(definition, mode, options, download) { - const handler = audioUrlBuilders.get(mode); - if (typeof handler === 'function') { - try { - return await handler(definition, options, download); - } catch (e) { - // NOP - } } - return null; -} - -function audioUrlNormalize(url, baseUrl, basePath) { - if (url) { - if (url[0] === '/') { - if (url.length >= 2 && url[1] === '/') { - // Begins with "//" - url = baseUrl.substring(0, baseUrl.indexOf(':') + 1) + url; - } else { - // Begins with "/" - url = baseUrl + url; - } - } else if (!/^[a-z][a-z0-9\-+.]*:/i.test(url)) { - // No URI scheme => relative path - url = baseUrl + basePath + url; - } - } - return url; } diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 1fdc4c70..66378b0c 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -21,9 +21,8 @@ conditionsTestValue, profileConditionsDescriptor handlebarsRenderDynamic requestText, requestJson, optionsLoad dictConfigured, dictTermsSort, dictEnabledSet -audioGetUrl jpConvertReading, jpDistributeFuriganaInflected, jpKatakanaToHiragana -AnkiNoteBuilder, AudioSystem, Translator, AnkiConnect, AnkiNull, Mecab, BackendApiForwarder, JsonSchema, ClipboardMonitor*/ +AnkiNoteBuilder, AudioSystem, AudioUriBuilder, Translator, AnkiConnect, AnkiNull, Mecab, BackendApiForwarder, JsonSchema, ClipboardMonitor*/ class Backend { constructor() { @@ -36,6 +35,7 @@ class Backend { this.optionsSchema = null; this.defaultAnkiFieldTemplates = null; this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); + this.audioUriBuilder = new AudioUriBuilder(); this.optionsContext = { depth: 0, url: window.location.href @@ -515,7 +515,7 @@ class Backend { async _onApiAudioGetUrl({definition, source, optionsContext}) { const options = this.getOptions(optionsContext); - return await audioGetUrl(definition, source, options); + return await this.audioUriBuilder.getUri(source, definition, options); } _onApiScreenshotGet({options}, sender) { @@ -771,7 +771,7 @@ class Backend { } const options = this.getOptions(optionsContext); - return await audioGetUrl(definition, source, options); + return await this.audioUriBuilder.getUri(source, definition, options); } async _audioInject(definition, fields, sources, optionsContext) { From aad4ab5eccaeed14514d676c0de4f3e2db718072 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Mar 2020 14:37:44 -0500 Subject: [PATCH 3/5] Rename audio functions using "url" to use "uri" --- ext/bg/js/backend.js | 4 ++-- ext/bg/js/settings/audio.js | 4 ++-- ext/mixed/js/api.js | 4 ++-- ext/mixed/js/display.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 66378b0c..eb88a6c1 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -67,7 +67,7 @@ class Backend { ['noteView', this._onApiNoteView.bind(this)], ['templateRender', this._onApiTemplateRender.bind(this)], ['commandExec', this._onApiCommandExec.bind(this)], - ['audioGetUrl', this._onApiAudioGetUrl.bind(this)], + ['audioGetUri', this._onApiAudioGetUri.bind(this)], ['screenshotGet', this._onApiScreenshotGet.bind(this)], ['forward', this._onApiForward.bind(this)], ['frameInformationGet', this._onApiFrameInformationGet.bind(this)], @@ -513,7 +513,7 @@ class Backend { return this._runCommand(command, params); } - async _onApiAudioGetUrl({definition, source, optionsContext}) { + async _onApiAudioGetUri({definition, source, optionsContext}) { const options = this.getOptions(optionsContext); return await this.audioUriBuilder.getUri(source, definition, options); } diff --git a/ext/bg/js/settings/audio.js b/ext/bg/js/settings/audio.js index 6f581d9b..c825be6b 100644 --- a/ext/bg/js/settings/audio.js +++ b/ext/bg/js/settings/audio.js @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -/*global getOptionsContext, getOptionsMutable, settingsSaveOptions, apiAudioGetUrl +/*global getOptionsContext, getOptionsMutable, settingsSaveOptions, apiAudioGetUri AudioSystem, AudioSourceUI*/ let audioSourceUI = null; @@ -26,7 +26,7 @@ async function audioSettingsInitialize() { audioSystem = new AudioSystem({ getAudioUri: async (definition, source) => { const optionsContext = getOptionsContext(); - return await apiAudioGetUrl(definition, source, optionsContext); + return await apiAudioGetUri(definition, source, optionsContext); } }); diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 26f4389d..0ab07039 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -69,8 +69,8 @@ function apiTemplateRender(template, data) { return _apiInvoke('templateRender', {data, template}); } -function apiAudioGetUrl(definition, source, optionsContext) { - return _apiInvoke('audioGetUrl', {definition, source, optionsContext}); +function apiAudioGetUri(definition, source, optionsContext) { + return _apiInvoke('audioGetUri', {definition, source, optionsContext}); } function apiCommandExec(command, params) { diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 3fe8e684..a220c1f7 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -18,7 +18,7 @@ /*global docRangeFromPoint, docSentenceExtract apiKanjiFind, apiTermsFind, apiNoteView, apiOptionsGet, apiDefinitionsAddable, apiDefinitionAdd -apiScreenshotGet, apiForward, apiAudioGetUrl +apiScreenshotGet, apiForward, apiAudioGetUri AudioSystem, DisplayGenerator, WindowScroll, DisplayContext, DOM*/ class Display { @@ -919,6 +919,6 @@ class Display { async _getAudioUri(definition, source) { const optionsContext = this.getOptionsContext(); - return await apiAudioGetUrl(definition, source, optionsContext); + return await apiAudioGetUri(definition, source, optionsContext); } } From 9cd4a52b9e701f47c7ca7b44c52cbcd66b7bcb05 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 7 Mar 2020 14:39:25 -0500 Subject: [PATCH 4/5] Rename audio.js to audio-uri-builder.js --- ext/bg/background.html | 2 +- ext/bg/js/{audio.js => audio-uri-builder.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename ext/bg/js/{audio.js => audio-uri-builder.js} (100%) diff --git a/ext/bg/background.html b/ext/bg/background.html index f6e00bf5..44abe8fd 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -24,7 +24,7 @@ - + diff --git a/ext/bg/js/audio.js b/ext/bg/js/audio-uri-builder.js similarity index 100% rename from ext/bg/js/audio.js rename to ext/bg/js/audio-uri-builder.js From 0cbf427ab50061b48c9027e63e9ee8a209946d37 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 9 Mar 2020 21:00:57 -0400 Subject: [PATCH 5/5] Update argument order --- ext/bg/js/audio-uri-builder.js | 4 ++-- ext/bg/js/backend.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/bg/js/audio-uri-builder.js b/ext/bg/js/audio-uri-builder.js index 80e9cb9a..15cea995 100644 --- a/ext/bg/js/audio-uri-builder.js +++ b/ext/bg/js/audio-uri-builder.js @@ -48,8 +48,8 @@ class AudioUriBuilder { return url; } - async getUri(mode, definition, options) { - const handler = this._getUrlHandlers.get(mode); + async getUri(definition, source, options) { + const handler = this._getUrlHandlers.get(source); if (typeof handler === 'function') { try { return await handler(definition, options); diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index eb88a6c1..adc6f13d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -515,7 +515,7 @@ class Backend { async _onApiAudioGetUri({definition, source, optionsContext}) { const options = this.getOptions(optionsContext); - return await this.audioUriBuilder.getUri(source, definition, options); + return await this.audioUriBuilder.getUri(definition, source, options); } _onApiScreenshotGet({options}, sender) { @@ -771,7 +771,7 @@ class Backend { } const options = this.getOptions(optionsContext); - return await this.audioUriBuilder.getUri(source, definition, options); + return await this.audioUriBuilder.getUri(definition, source, options); } async _audioInject(definition, fields, sources, optionsContext) {