support for multiple audio sources, version bump
This commit is contained in:
parent
4eb3e2f06c
commit
9ac55fb5d1
@ -25,7 +25,7 @@ function formRead() {
|
||||
return optionsLoad().then(optionsOld => {
|
||||
const optionsNew = $.extend(true, {}, optionsOld);
|
||||
|
||||
optionsNew.general.audioPlayback = $('#audio-playback-buttons').prop('checked');
|
||||
optionsNew.general.audioSource = $('#audio-playback-source').val();
|
||||
optionsNew.general.audioVolume = $('#audio-playback-volume').val();
|
||||
optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
|
||||
optionsNew.general.softKatakana = $('#soft-katakana-search').prop('checked');
|
||||
@ -111,7 +111,7 @@ $(document).ready(() => {
|
||||
handlebarsRegister();
|
||||
|
||||
optionsLoad().then(options => {
|
||||
$('#audio-playback-buttons').prop('checked', options.general.audioPlayback);
|
||||
$('#audio-playback-source').val(options.general.audioSource);
|
||||
$('#audio-playback-volume').val(options.general.audioVolume);
|
||||
$('#group-terms-results').prop('checked', options.general.groupResults);
|
||||
$('#soft-katakana-search').prop('checked', options.general.softKatakana);
|
||||
|
@ -25,9 +25,6 @@ function promiseCallback(promise, callback) {
|
||||
return promise.then(result => {
|
||||
callback({result});
|
||||
}).catch(error => {
|
||||
/* eslint-disable */
|
||||
console.log(error);
|
||||
/* eslint-enable */
|
||||
callback({error});
|
||||
});
|
||||
}
|
||||
@ -84,7 +81,7 @@ function optionsSetDefaults(options) {
|
||||
const defaults = {
|
||||
general: {
|
||||
enable: true,
|
||||
audioPlayback: true,
|
||||
audioSource: 'jpod101',
|
||||
audioVolume: 100,
|
||||
groupResults: true,
|
||||
softKatakana: true,
|
||||
@ -137,84 +134,15 @@ function optionsSetDefaults(options) {
|
||||
|
||||
function optionsVersion(options) {
|
||||
const fixups = [
|
||||
() => { },
|
||||
() => { },
|
||||
() => { },
|
||||
() => { },
|
||||
() => {
|
||||
const copy = (targetDict, targetKey, sourceDict, sourceKey) => {
|
||||
targetDict[targetKey] = sourceDict.hasOwnProperty(sourceKey) ? sourceDict[sourceKey] : targetDict[targetKey];
|
||||
};
|
||||
|
||||
copy(options.general, 'autoStart', options, 'activateOnStartup');
|
||||
copy(options.general, 'audioPlayback', options, 'enableAudioPlayback');
|
||||
copy(options.general, 'softKatakana', options, 'enableSoftKatakanaSearch');
|
||||
copy(options.general, 'groupResults', options, 'groupTermResults');
|
||||
copy(options.general, 'showAdvanced', options, 'showAdvancedOptions');
|
||||
|
||||
copy(options.scanning, 'requireShift', options, 'holdShiftToScan');
|
||||
copy(options.scanning, 'selectText', options, 'selectMatchedText');
|
||||
copy(options.scanning, 'delay', options, 'scanDelay');
|
||||
copy(options.scanning, 'length', options, 'scanLength');
|
||||
|
||||
options.anki.enable = options.ankiMethod === 'ankiconnect';
|
||||
|
||||
copy(options.anki, 'tags', options, 'ankiCardTags');
|
||||
copy(options.anki, 'sentenceExt', options, 'sentenceExtent');
|
||||
copy(options.anki.terms, 'deck', options, 'ankiTermDeck');
|
||||
copy(options.anki.terms, 'model', options, 'ankiTermModel');
|
||||
copy(options.anki.terms, 'fields', options, 'ankiTermFields');
|
||||
copy(options.anki.kanji, 'deck', options, 'ankiKanjiDeck');
|
||||
copy(options.anki.kanji, 'model', options, 'ankiKanjiModel');
|
||||
copy(options.anki.kanji, 'fields', options, 'ankiKanjiFields');
|
||||
|
||||
for (const title in options.dictionaries) {
|
||||
const dictionary = options.dictionaries[title];
|
||||
dictionary.enabled = dictionary.enableTerms || dictionary.enableKanji;
|
||||
dictionary.priority = 0;
|
||||
}
|
||||
},
|
||||
() => {
|
||||
const fixupFields = fields => {
|
||||
const fixups = {
|
||||
'{expression-furigana}': '{furigana}',
|
||||
'{glossary-list}': '{glossary}'
|
||||
};
|
||||
|
||||
for (const name in fields) {
|
||||
for (const fixup in fixups) {
|
||||
fields[name] = fields[name].replace(fixup, fixups[fixup]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fixupFields(options.anki.terms.fields);
|
||||
fixupFields(options.anki.kanji.fields);
|
||||
},
|
||||
() => {
|
||||
let hasEnabledDict = false;
|
||||
for (const title in options.dictionaries) {
|
||||
if (options.dictionaries[title].enabled) {
|
||||
hasEnabledDict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasEnabledDict) {
|
||||
for (const title in options.dictionaries) {
|
||||
options.dictionaries[title].enabled = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
let hasEnabledDict = false;
|
||||
for (const title in options.dictionaries) {
|
||||
if (options.dictionaries[title].enabled) {
|
||||
hasEnabledDict = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasEnabledDict) {
|
||||
for (const title in options.dictionaries) {
|
||||
options.dictionaries[title].enabled = true;
|
||||
}
|
||||
if (options.general.audioPlayback) {
|
||||
options.general.audioSource = 'jpod101';
|
||||
} else {
|
||||
options.general.audioSource = 'disabled';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -121,7 +121,7 @@ window.yomichan = new class {
|
||||
definitionAdd(definition, mode) {
|
||||
let promise = Promise.resolve();
|
||||
if (mode !== 'kanji') {
|
||||
promise = audioInject(definition, this.options.anki.terms.fields);
|
||||
promise = audioInject(definition, this.options.anki.terms.fields, this.options.general.audioSource);
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
|
@ -37,8 +37,13 @@
|
||||
<label><input type="checkbox" id="soft-katakana-search"> Soft Katakana search</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label><input type="checkbox" id="audio-playback-buttons"> Audio playback buttons</label>
|
||||
<div class="form-group">
|
||||
<label for="audio-playback-source">Audio playback source</label>
|
||||
<select class="form-control" id="audio-playback-source">
|
||||
<option value="disabled">Disabled</option>
|
||||
<option value="jpod101">JapanesePod101</option>
|
||||
<option value="jpod101-alternate">JapanesePod101 (Alternate)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group options-advanced">
|
||||
@ -52,7 +57,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group options-advanced">
|
||||
<label>Popup size (in pixels)</label>
|
||||
<label>Popup size (width x height, in pixels)</label>
|
||||
<div class="row">
|
||||
<div class="col-xs-6"><input type="number" min="1" id="popup-width" class="form-control"></div>
|
||||
<div class="col-xs-6"><input type="number" min="1" id="popup-height" class="form-control"></div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Yomichan",
|
||||
"version": "1.1.10",
|
||||
"version": "1.1.11",
|
||||
|
||||
"description": "Japanese dictionary with Anki integration",
|
||||
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
|
||||
|
@ -69,7 +69,7 @@ class Display {
|
||||
definitions,
|
||||
addable: options.anki.enable,
|
||||
grouped: options.general.groupResults,
|
||||
playback: options.general.audioPlayback
|
||||
playback: options.general.audioSource !== 'disabled'
|
||||
};
|
||||
|
||||
if (context) {
|
||||
@ -335,7 +335,7 @@ class Display {
|
||||
this.audioCache[key].pause();
|
||||
}
|
||||
|
||||
audioBuildUrl(definition, this.responseCache).then(url => {
|
||||
audioBuildUrl(definition, this.options.general.audioSource, this.responseCache).then(url => {
|
||||
if (!url) {
|
||||
url = '/mixed/mp3/button.mp3';
|
||||
}
|
||||
|
@ -21,48 +21,71 @@
|
||||
* Audio
|
||||
*/
|
||||
|
||||
function audioBuildUrl(definition, cache={}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const response = cache[definition.expression];
|
||||
if (response) {
|
||||
resolve(response);
|
||||
} else {
|
||||
const data = {
|
||||
post: 'dictionary_reference',
|
||||
match_type: 'exact',
|
||||
search_query: definition.expression
|
||||
};
|
||||
function audioBuildUrl(definition, mode, cache={}) {
|
||||
if (mode === 'jpod101') {
|
||||
let kana = definition.reading;
|
||||
let kanji = definition.expression;
|
||||
|
||||
const params = [];
|
||||
for (const key in data) {
|
||||
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
|
||||
}
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post');
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
xhr.addEventListener('error', () => reject('failed to scrape audio data'));
|
||||
xhr.addEventListener('load', () => {
|
||||
cache[definition.expression] = xhr.responseText;
|
||||
resolve(xhr.responseText);
|
||||
});
|
||||
|
||||
xhr.send(params.join('&'));
|
||||
if (!kana && wanakana.isHiragana(kanji)) {
|
||||
kana = kanji;
|
||||
kanji = null;
|
||||
}
|
||||
}).then(response => {
|
||||
const dom = new DOMParser().parseFromString(response, 'text/html');
|
||||
for (const row of dom.getElementsByClassName('dc-result-row')) {
|
||||
try {
|
||||
const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url');
|
||||
const reading = row.getElementsByClassName('dc-vocab_kana').item(0).innerText;
|
||||
if (url && reading && (!definition.reading || definition.reading === reading)) {
|
||||
return url;
|
||||
|
||||
const params = [];
|
||||
if (kanji) {
|
||||
params.push(`kanji=${encodeURIComponent(kanji)}`);
|
||||
}
|
||||
if (kana) {
|
||||
params.push(`kana=${encodeURIComponent(kana)}`);
|
||||
}
|
||||
|
||||
const url = `https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?${params.join('&')}`;
|
||||
return Promise.resolve(url);
|
||||
} else if (mode === 'jpod101-alternate') {
|
||||
return new Promise((resolve, reject) => {
|
||||
const response = cache[definition.expression];
|
||||
if (response) {
|
||||
resolve(response);
|
||||
} else {
|
||||
const data = {
|
||||
post: 'dictionary_reference',
|
||||
match_type: 'exact',
|
||||
search_query: definition.expression
|
||||
};
|
||||
|
||||
const params = [];
|
||||
for (const key in data) {
|
||||
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
|
||||
}
|
||||
} catch (e) {
|
||||
// NOP
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'https://www.japanesepod101.com/learningcenter/reference/dictionary_post');
|
||||
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
|
||||
xhr.addEventListener('error', () => reject('failed to scrape audio data'));
|
||||
xhr.addEventListener('load', () => {
|
||||
cache[definition.expression] = xhr.responseText;
|
||||
resolve(xhr.responseText);
|
||||
});
|
||||
|
||||
xhr.send(params.join('&'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}).then(response => {
|
||||
const dom = new DOMParser().parseFromString(response, 'text/html');
|
||||
for (const row of dom.getElementsByClassName('dc-result-row')) {
|
||||
try {
|
||||
const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url');
|
||||
const reading = row.getElementsByClassName('dc-vocab_kana').item(0).innerText;
|
||||
if (url && reading && (!definition.reading || definition.reading === reading)) {
|
||||
return url;
|
||||
}
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Promise.reject('unsupported audio source');
|
||||
}
|
||||
}
|
||||
|
||||
function audioBuildFilename(definition) {
|
||||
@ -79,7 +102,7 @@ function audioBuildFilename(definition) {
|
||||
}
|
||||
}
|
||||
|
||||
function audioInject(definition, fields) {
|
||||
function audioInject(definition, fields, mode) {
|
||||
const filename = audioBuildFilename(definition);
|
||||
if (!filename) {
|
||||
return Promise.resolve(true);
|
||||
@ -97,7 +120,7 @@ function audioInject(definition, fields) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
return audioBuildUrl(definition).then(url => {
|
||||
return audioBuildUrl(definition, mode).then(url => {
|
||||
definition.audio = {url, filename};
|
||||
return true;
|
||||
}).catch(() => false);
|
||||
|
Loading…
Reference in New Issue
Block a user