Audio system refactoring (#1275)

* Simplify details

* Simplify audio creation

* Return an array of sources instead of a single item

* Use sourceIndex instead of index

* Rename APIs

* Return more info about the source

* Return source instead of sourceIndex
This commit is contained in:
toasted-nutbread 2021-01-18 22:01:08 -05:00 committed by GitHub
parent 85c723b85f
commit 21fce9f3d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 65 deletions

View File

@ -34,7 +34,7 @@ class AudioDownloader {
]); ]);
} }
async getInfo(source, expression, reading, details) { async getExpressionAudioInfoList(source, expression, reading, details) {
const handler = this._getInfoHandlers.get(source); const handler = this._getInfoHandlers.get(source);
if (typeof handler === 'function') { if (typeof handler === 'function') {
try { try {
@ -43,23 +43,22 @@ class AudioDownloader {
// NOP // NOP
} }
} }
return null; return [];
} }
async downloadAudio(sources, expression, reading, details) { async downloadExpressionAudio(sources, expression, reading, details) {
for (const source of sources) { for (const source of sources) {
const info = await this.getInfo(source, expression, reading, details); const infoList = await this.getExpressionAudioInfoList(source, expression, reading, details);
if (info === null) { continue; } for (const info of infoList) {
switch (info.type) {
switch (info.type) { case 'url':
case 'url': try {
try { return await this._downloadAudioFromUrl(info.url);
const {details: {url}} = info; } catch (e) {
return await this._downloadAudioFromUrl(url); // NOP
} catch (e) { }
// NOP break;
} }
break;
} }
} }
@ -90,7 +89,7 @@ class AudioDownloader {
} }
const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`; const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`;
return {type: 'url', details: {url}}; return [{type: 'url', url}];
} }
async _getInfoJpod101Alternate(expression, reading) { async _getInfoJpod101Alternate(expression, reading) {
@ -128,7 +127,7 @@ class AudioDownloader {
const htmlReading = dom.getTextContent(htmlReadings[0]); const htmlReading = dom.getTextContent(htmlReadings[0]);
if (htmlReading && (!reading || reading === htmlReading)) { if (htmlReading && (!reading || reading === htmlReading)) {
url = this._normalizeUrl(url, response.url); url = this._normalizeUrl(url, response.url);
return {type: 'url', details: {url}}; return [{type: 'url', url}];
} }
} catch (e) { } catch (e) {
// NOP // NOP
@ -159,7 +158,7 @@ class AudioDownloader {
let url = dom.getAttribute(source, 'src'); let url = dom.getAttribute(source, 'src');
if (url !== null) { if (url !== null) {
url = this._normalizeUrl(url, response.url); url = this._normalizeUrl(url, response.url);
return {type: 'url', details: {url}}; return [{type: 'url', url}];
} }
} }
} }
@ -174,14 +173,14 @@ class AudioDownloader {
if (!textToSpeechVoice) { if (!textToSpeechVoice) {
throw new Error('No voice'); throw new Error('No voice');
} }
return {type: 'tts', details: {text: expression, voice: textToSpeechVoice}}; return [{type: 'tts', text: expression, voice: textToSpeechVoice}];
} }
async _getInfoTextToSpeechReading(expression, reading, {textToSpeechVoice}) { async _getInfoTextToSpeechReading(expression, reading, {textToSpeechVoice}) {
if (!textToSpeechVoice) { if (!textToSpeechVoice) {
throw new Error('No voice'); throw new Error('No voice');
} }
return {type: 'tts', details: {text: reading || expression, voice: textToSpeechVoice}}; return [{type: 'tts', text: reading || expression, voice: textToSpeechVoice}];
} }
async _getInfoCustom(expression, reading, {customSourceUrl}) { async _getInfoCustom(expression, reading, {customSourceUrl}) {
@ -190,7 +189,7 @@ class AudioDownloader {
} }
const data = {expression, reading}; const data = {expression, reading};
const url = customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (Object.prototype.hasOwnProperty.call(data, m1) ? `${data[m1]}` : m0)); const url = customSourceUrl.replace(/\{([^}]*)\}/g, (m0, m1) => (Object.prototype.hasOwnProperty.call(data, m1) ? `${data[m1]}` : m0));
return {type: 'url', details: {url}}; return [{type: 'url', url}];
} }
async _downloadAudioFromUrl(url) { async _downloadAudioFromUrl(url) {

View File

@ -99,7 +99,7 @@ class Backend {
['noteView', {async: true, contentScript: true, handler: this._onApiNoteView.bind(this)}], ['noteView', {async: true, contentScript: true, handler: this._onApiNoteView.bind(this)}],
['suspendAnkiCardsForNote', {async: true, contentScript: true, handler: this._onApiSuspendAnkiCardsForNote.bind(this)}], ['suspendAnkiCardsForNote', {async: true, contentScript: true, handler: this._onApiSuspendAnkiCardsForNote.bind(this)}],
['commandExec', {async: false, contentScript: true, handler: this._onApiCommandExec.bind(this)}], ['commandExec', {async: false, contentScript: true, handler: this._onApiCommandExec.bind(this)}],
['getDefinitionAudioInfo', {async: true, contentScript: true, handler: this._onApiGetDefinitionAudioInfo.bind(this)}], ['getExpressionAudioInfoList', {async: true, contentScript: true, handler: this._onApiGetExpressionAudioInfoList.bind(this)}],
['downloadDefinitionAudio', {async: true, contentScript: true, handler: this._onApiDownloadDefinitionAudio.bind(this)}], ['downloadDefinitionAudio', {async: true, contentScript: true, handler: this._onApiDownloadDefinitionAudio.bind(this)}],
['screenshotGet', {async: true, contentScript: true, handler: this._onApiScreenshotGet.bind(this)}], ['screenshotGet', {async: true, contentScript: true, handler: this._onApiScreenshotGet.bind(this)}],
['sendMessageToFrame', {async: false, contentScript: true, handler: this._onApiSendMessageToFrame.bind(this)}], ['sendMessageToFrame', {async: false, contentScript: true, handler: this._onApiSendMessageToFrame.bind(this)}],
@ -500,8 +500,8 @@ class Backend {
return this._runCommand(command, params); return this._runCommand(command, params);
} }
async _onApiGetDefinitionAudioInfo({source, expression, reading, details}) { async _onApiGetExpressionAudioInfoList({source, expression, reading, details}) {
return await this._audioDownloader.getInfo(source, expression, reading, details); return await this._audioDownloader.getExpressionAudioInfoList(source, expression, reading, details);
} }
async _onApiDownloadDefinitionAudio({sources, expression, reading, details}) { async _onApiDownloadDefinitionAudio({sources, expression, reading, details}) {
@ -1528,7 +1528,7 @@ class Backend {
} }
async _downloadDefinitionAudio(sources, expression, reading, details) { async _downloadDefinitionAudio(sources, expression, reading, details) {
return await this._audioDownloader.downloadAudio(sources, expression, reading, details); return await this._audioDownloader.downloadExpressionAudio(sources, expression, reading, details);
} }
async _injectAnkNoteMedia(ankiConnect, timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) { async _injectAnkNoteMedia(ankiConnect, timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) {

View File

@ -97,8 +97,8 @@ const api = (() => {
return this._invoke('suspendAnkiCardsForNote', {noteId}); return this._invoke('suspendAnkiCardsForNote', {noteId});
} }
getDefinitionAudioInfo(source, expression, reading, details) { getExpressionAudioInfoList(source, expression, reading, details) {
return this._invoke('getDefinitionAudioInfo', {source, expression, reading, details}); return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details});
} }
downloadDefinitionAudio(sources, expression, reading, details) { downloadDefinitionAudio(sources, expression, reading, details) {

View File

@ -36,47 +36,30 @@ class AudioSystem {
eventListeners.addEventListener(speechSynthesis, 'voiceschanged', onVoicesChanged, false); eventListeners.addEventListener(speechSynthesis, 'voiceschanged', onVoicesChanged, false);
} }
async createDefinitionAudio(sources, expression, reading, details) { async createExpressionAudio(sources, expression, reading, details) {
const key = [expression, reading]; const key = [expression, reading];
const cacheValue = this._cache.get(key); const cacheValue = this._cache.get(key);
if (typeof cacheValue !== 'undefined') { if (typeof cacheValue !== 'undefined') {
const {audio, source} = cacheValue; return cacheValue;
const index = sources.indexOf(source);
if (index >= 0) {
return {audio, index};
}
} }
for (let i = 0, ii = sources.length; i < ii; ++i) { for (let i = 0, ii = sources.length; i < ii; ++i) {
const source = sources[i]; const source = sources[i];
const info = await this._getAudioInfo(source, expression, reading, details); const infoList = await await api.getExpressionAudioInfoList(source, expression, reading, details);
if (info === null) { continue; } for (let j = 0, jj = infoList.length; j < jj; ++j) {
const info = infoList[j];
let audio; let audio;
try { try {
switch (info.type) { audio = await this.createAudioFromInfo(info);
case 'url': } catch (e) {
{ continue;
const {details: {url}} = info;
audio = await this.createAudio(url);
}
break;
case 'tts':
{
const {details: {text, voice}} = info;
audio = this.createTextToSpeechAudio(text, voice);
}
break;
default:
throw new Error(`Unsupported type: ${info.type}`);
} }
} catch (e) {
continue;
}
this._cache.set(key, {audio, source}); const result = {audio, source, infoList, infoListIndex: j};
return {audio, index: i}; this._cache.set(key, result);
return result;
}
} }
throw new Error('Could not create audio'); throw new Error('Could not create audio');
@ -111,12 +94,19 @@ class AudioSystem {
return new TextToSpeechAudio(text, voice); return new TextToSpeechAudio(text, voice);
} }
// Private async createAudioFromInfo(info) {
switch (info.type) {
async _getAudioInfo(source, expression, reading, details) { case 'url':
return await api.getDefinitionAudioInfo(source, expression, reading, details); return await this.createAudio(info.url);
case 'tts':
return this.createTextToSpeechAudio(info.text, info.voice);
default:
throw new Error(`Unsupported type: ${info.type}`);
}
} }
// Private
_isAudioValid(audio) { _isAudioValid(audio) {
const duration = audio.duration; const duration = audio.duration;
return ( return (

View File

@ -117,9 +117,10 @@ class DisplayAudio {
let audio; let audio;
let info; let info;
try { try {
let index; let source;
({audio, index} = await this._audioSystem.createDefinitionAudio(sources, expression, reading, {textToSpeechVoice, customSourceUrl})); ({audio, source} = await this._audioSystem.createExpressionAudio(sources, expression, reading, {textToSpeechVoice, customSourceUrl}));
info = `From source ${1 + index}: ${sources[index]}`; const sourceIndex = sources.indexOf(source);
info = `From source ${1 + sourceIndex}: ${source}`;
} catch (e) { } catch (e) {
audio = this._audioSystem.getFallbackAudio(); audio = this._audioSystem.getFallbackAudio();
info = 'Could not find audio'; info = 'Could not find audio';