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:
parent
85c723b85f
commit
21fce9f3d9
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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) {
|
||||||
|
@ -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 (
|
||||||
|
@ -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';
|
||||||
|
Loading…
Reference in New Issue
Block a user