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 => {
|
return optionsLoad().then(optionsOld => {
|
||||||
const optionsNew = $.extend(true, {}, 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.audioVolume = $('#audio-playback-volume').val();
|
||||||
optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
|
optionsNew.general.groupResults = $('#group-terms-results').prop('checked');
|
||||||
optionsNew.general.softKatakana = $('#soft-katakana-search').prop('checked');
|
optionsNew.general.softKatakana = $('#soft-katakana-search').prop('checked');
|
||||||
@ -111,7 +111,7 @@ $(document).ready(() => {
|
|||||||
handlebarsRegister();
|
handlebarsRegister();
|
||||||
|
|
||||||
optionsLoad().then(options => {
|
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);
|
$('#audio-playback-volume').val(options.general.audioVolume);
|
||||||
$('#group-terms-results').prop('checked', options.general.groupResults);
|
$('#group-terms-results').prop('checked', options.general.groupResults);
|
||||||
$('#soft-katakana-search').prop('checked', options.general.softKatakana);
|
$('#soft-katakana-search').prop('checked', options.general.softKatakana);
|
||||||
|
@ -25,9 +25,6 @@ function promiseCallback(promise, callback) {
|
|||||||
return promise.then(result => {
|
return promise.then(result => {
|
||||||
callback({result});
|
callback({result});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
/* eslint-disable */
|
|
||||||
console.log(error);
|
|
||||||
/* eslint-enable */
|
|
||||||
callback({error});
|
callback({error});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -84,7 +81,7 @@ function optionsSetDefaults(options) {
|
|||||||
const defaults = {
|
const defaults = {
|
||||||
general: {
|
general: {
|
||||||
enable: true,
|
enable: true,
|
||||||
audioPlayback: true,
|
audioSource: 'jpod101',
|
||||||
audioVolume: 100,
|
audioVolume: 100,
|
||||||
groupResults: true,
|
groupResults: true,
|
||||||
softKatakana: true,
|
softKatakana: true,
|
||||||
@ -137,84 +134,15 @@ function optionsSetDefaults(options) {
|
|||||||
|
|
||||||
function optionsVersion(options) {
|
function optionsVersion(options) {
|
||||||
const fixups = [
|
const fixups = [
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
|
() => { },
|
||||||
() => {
|
() => {
|
||||||
const copy = (targetDict, targetKey, sourceDict, sourceKey) => {
|
if (options.general.audioPlayback) {
|
||||||
targetDict[targetKey] = sourceDict.hasOwnProperty(sourceKey) ? sourceDict[sourceKey] : targetDict[targetKey];
|
options.general.audioSource = 'jpod101';
|
||||||
};
|
} else {
|
||||||
|
options.general.audioSource = 'disabled';
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -121,7 +121,7 @@ window.yomichan = new class {
|
|||||||
definitionAdd(definition, mode) {
|
definitionAdd(definition, mode) {
|
||||||
let promise = Promise.resolve();
|
let promise = Promise.resolve();
|
||||||
if (mode !== 'kanji') {
|
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(() => {
|
return promise.then(() => {
|
||||||
|
@ -37,8 +37,13 @@
|
|||||||
<label><input type="checkbox" id="soft-katakana-search"> Soft Katakana search</label>
|
<label><input type="checkbox" id="soft-katakana-search"> Soft Katakana search</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="checkbox">
|
<div class="form-group">
|
||||||
<label><input type="checkbox" id="audio-playback-buttons"> Audio playback buttons</label>
|
<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>
|
||||||
|
|
||||||
<div class="form-group options-advanced">
|
<div class="form-group options-advanced">
|
||||||
@ -52,7 +57,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group options-advanced">
|
<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="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-width" class="form-control"></div>
|
||||||
<div class="col-xs-6"><input type="number" min="1" id="popup-height" 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,
|
"manifest_version": 2,
|
||||||
"name": "Yomichan",
|
"name": "Yomichan",
|
||||||
"version": "1.1.10",
|
"version": "1.1.11",
|
||||||
|
|
||||||
"description": "Japanese dictionary with Anki integration",
|
"description": "Japanese dictionary with Anki integration",
|
||||||
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
|
"icons": {"16": "mixed/img/icon16.png", "48": "mixed/img/icon48.png", "128": "mixed/img/icon128.png"},
|
||||||
|
@ -69,7 +69,7 @@ class Display {
|
|||||||
definitions,
|
definitions,
|
||||||
addable: options.anki.enable,
|
addable: options.anki.enable,
|
||||||
grouped: options.general.groupResults,
|
grouped: options.general.groupResults,
|
||||||
playback: options.general.audioPlayback
|
playback: options.general.audioSource !== 'disabled'
|
||||||
};
|
};
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
@ -335,7 +335,7 @@ class Display {
|
|||||||
this.audioCache[key].pause();
|
this.audioCache[key].pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
audioBuildUrl(definition, this.responseCache).then(url => {
|
audioBuildUrl(definition, this.options.general.audioSource, this.responseCache).then(url => {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
url = '/mixed/mp3/button.mp3';
|
url = '/mixed/mp3/button.mp3';
|
||||||
}
|
}
|
||||||
|
@ -21,48 +21,71 @@
|
|||||||
* Audio
|
* Audio
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function audioBuildUrl(definition, cache={}) {
|
function audioBuildUrl(definition, mode, cache={}) {
|
||||||
return new Promise((resolve, reject) => {
|
if (mode === 'jpod101') {
|
||||||
const response = cache[definition.expression];
|
let kana = definition.reading;
|
||||||
if (response) {
|
let kanji = definition.expression;
|
||||||
resolve(response);
|
|
||||||
} else {
|
|
||||||
const data = {
|
|
||||||
post: 'dictionary_reference',
|
|
||||||
match_type: 'exact',
|
|
||||||
search_query: definition.expression
|
|
||||||
};
|
|
||||||
|
|
||||||
const params = [];
|
if (!kana && wanakana.isHiragana(kanji)) {
|
||||||
for (const key in data) {
|
kana = kanji;
|
||||||
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`);
|
kanji = null;
|
||||||
}
|
|
||||||
|
|
||||||
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');
|
const params = [];
|
||||||
for (const row of dom.getElementsByClassName('dc-result-row')) {
|
if (kanji) {
|
||||||
try {
|
params.push(`kanji=${encodeURIComponent(kanji)}`);
|
||||||
const url = row.getElementsByClassName('ill-onebuttonplayer').item(0).getAttribute('data-url');
|
}
|
||||||
const reading = row.getElementsByClassName('dc-vocab_kana').item(0).innerText;
|
if (kana) {
|
||||||
if (url && reading && (!definition.reading || definition.reading === reading)) {
|
params.push(`kana=${encodeURIComponent(kana)}`);
|
||||||
return url;
|
}
|
||||||
|
|
||||||
|
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) {
|
function audioBuildFilename(definition) {
|
||||||
@ -79,7 +102,7 @@ function audioBuildFilename(definition) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function audioInject(definition, fields) {
|
function audioInject(definition, fields, mode) {
|
||||||
const filename = audioBuildFilename(definition);
|
const filename = audioBuildFilename(definition);
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@ -97,7 +120,7 @@ function audioInject(definition, fields) {
|
|||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return audioBuildUrl(definition).then(url => {
|
return audioBuildUrl(definition, mode).then(url => {
|
||||||
definition.audio = {url, filename};
|
definition.audio = {url, filename};
|
||||||
return true;
|
return true;
|
||||||
}).catch(() => false);
|
}).catch(() => false);
|
||||||
|
Loading…
Reference in New Issue
Block a user