Update how audio URIs are built

This commit is contained in:
toasted-nutbread 2020-03-07 14:36:16 -05:00
parent 21d194d145
commit 391f3dd29a
2 changed files with 64 additions and 48 deletions

View File

@ -18,8 +18,49 @@
/*global jpIsStringEntirelyKana*/ /*global jpIsStringEntirelyKana*/
const audioUrlBuilders = new Map([ class AudioUriBuilder {
['jpod101', async (definition) => { 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 kana = definition.reading;
let kanji = definition.expression; let kanji = definition.expression;
@ -37,8 +78,9 @@ const audioUrlBuilders = new Map([
} }
return `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; 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 response = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post'); 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 url = row.querySelector('audio>source[src]').getAttribute('src');
const reading = row.getElementsByClassName('dc-vocab_kana').item(0).textContent; const reading = row.getElementsByClassName('dc-vocab_kana').item(0).textContent;
if (url && reading && (!definition.reading || definition.reading === reading)) { 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) { } catch (e) {
// NOP // NOP
@ -62,8 +104,9 @@ const audioUrlBuilders = new Map([
} }
throw new Error('Failed to find audio URL'); throw new Error('Failed to find audio URL');
}], }
['jisho', async (definition) => {
async _getUriJisho(definition) {
const response = await new Promise((resolve, reject) => { const response = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open('GET', `https://jisho.org/search/${definition.expression}`); xhr.open('GET', `https://jisho.org/search/${definition.expression}`);
@ -78,7 +121,7 @@ const audioUrlBuilders = new Map([
if (audio !== null) { if (audio !== null) {
const url = audio.getElementsByTagName('source').item(0).getAttribute('src'); const url = audio.getElementsByTagName('source').item(0).getAttribute('src');
if (url) { if (url) {
return audioUrlNormalize(url, 'https://jisho.org', '/search/'); return this.normalizeUrl(url, 'https://jisho.org', '/search/');
} }
} }
} catch (e) { } catch (e) {
@ -86,55 +129,28 @@ const audioUrlBuilders = new Map([
} }
throw new Error('Failed to find audio URL'); throw new Error('Failed to find audio URL');
}], }
['text-to-speech', async (definition, options) => {
async _getUriTextToSpeech(definition, options) {
const voiceURI = options.audio.textToSpeechVoice; const voiceURI = options.audio.textToSpeechVoice;
if (!voiceURI) { if (!voiceURI) {
throw new Error('No voice'); throw new Error('No voice');
} }
return `tts:?text=${encodeURIComponent(definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; 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; const voiceURI = options.audio.textToSpeechVoice;
if (!voiceURI) { if (!voiceURI) {
throw new Error('No voice'); throw new Error('No voice');
} }
return `tts:?text=${encodeURIComponent(definition.reading || definition.expression)}&voice=${encodeURIComponent(voiceURI)}`; return `tts:?text=${encodeURIComponent(definition.reading || definition.expression)}&voice=${encodeURIComponent(voiceURI)}`;
}], }
['custom', async (definition, options) => {
async _getUriCustom(definition, options) {
const customSourceUrl = options.audio.customSourceUrl; const customSourceUrl = options.audio.customSourceUrl;
return customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (hasOwn(definition, m1) ? `${definition[m1]}` : m0)); 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;
}

View File

@ -21,9 +21,8 @@ conditionsTestValue, profileConditionsDescriptor
handlebarsRenderDynamic handlebarsRenderDynamic
requestText, requestJson, optionsLoad requestText, requestJson, optionsLoad
dictConfigured, dictTermsSort, dictEnabledSet dictConfigured, dictTermsSort, dictEnabledSet
audioGetUrl
jpConvertReading, jpDistributeFuriganaInflected, jpKatakanaToHiragana jpConvertReading, jpDistributeFuriganaInflected, jpKatakanaToHiragana
AnkiNoteBuilder, AudioSystem, Translator, AnkiConnect, AnkiNull, Mecab, BackendApiForwarder, JsonSchema, ClipboardMonitor*/ AnkiNoteBuilder, AudioSystem, AudioUriBuilder, Translator, AnkiConnect, AnkiNull, Mecab, BackendApiForwarder, JsonSchema, ClipboardMonitor*/
class Backend { class Backend {
constructor() { constructor() {
@ -36,6 +35,7 @@ class Backend {
this.optionsSchema = null; this.optionsSchema = null;
this.defaultAnkiFieldTemplates = null; this.defaultAnkiFieldTemplates = null;
this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)});
this.audioUriBuilder = new AudioUriBuilder();
this.optionsContext = { this.optionsContext = {
depth: 0, depth: 0,
url: window.location.href url: window.location.href
@ -515,7 +515,7 @@ class Backend {
async _onApiAudioGetUrl({definition, source, optionsContext}) { async _onApiAudioGetUrl({definition, source, optionsContext}) {
const options = this.getOptions(optionsContext); const options = this.getOptions(optionsContext);
return await audioGetUrl(definition, source, options); return await this.audioUriBuilder.getUri(source, definition, options);
} }
_onApiScreenshotGet({options}, sender) { _onApiScreenshotGet({options}, sender) {
@ -771,7 +771,7 @@ class Backend {
} }
const options = this.getOptions(optionsContext); 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) { async _audioInject(definition, fields, sources, optionsContext) {