Merge pull request #453 from toasted-nutbread/reuse-anki-instance

Reuse AnkiConnect instance
This commit is contained in:
toasted-nutbread 2020-04-18 14:14:52 -04:00 committed by GitHub
commit 5b3d7fadc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 81 additions and 83 deletions

View File

@ -16,7 +16,8 @@
*/ */
class AnkiNoteBuilder { class AnkiNoteBuilder {
constructor({audioSystem, renderTemplate}) { constructor({anki, audioSystem, renderTemplate}) {
this._anki = anki;
this._audioSystem = audioSystem; this._audioSystem = audioSystem;
this._renderTemplate = renderTemplate; this._renderTemplate = renderTemplate;
} }
@ -101,7 +102,7 @@ class AnkiNoteBuilder {
} }
} }
async injectScreenshot(definition, fields, screenshot, anki) { async injectScreenshot(definition, fields, screenshot) {
if (!this._containsMarker(fields, 'screenshot')) { return; } if (!this._containsMarker(fields, 'screenshot')) { return; }
const now = new Date(Date.now()); const now = new Date(Date.now());
@ -109,7 +110,7 @@ class AnkiNoteBuilder {
const data = screenshot.dataUrl.replace(/^data:[\w\W]*?,/, ''); const data = screenshot.dataUrl.replace(/^data:[\w\W]*?,/, '');
try { try {
await anki.storeMediaFile(filename, data); await this._anki.storeMediaFile(filename, data);
} catch (e) { } catch (e) {
return; return;
} }

View File

@ -19,122 +19,119 @@
* requestJson * requestJson
*/ */
/*
* AnkiConnect
*/
class AnkiConnect { class AnkiConnect {
constructor(server) { constructor(server) {
this.server = server; this._enabled = false;
this.localVersion = 2; this._server = server;
this.remoteVersion = 0; this._localVersion = 2;
this._remoteVersion = 0;
}
setServer(server) {
this._server = server;
}
getServer() {
return this._server;
}
setEnabled(enabled) {
this._enabled = enabled;
}
isEnabled() {
return this._enabled;
} }
async addNote(note) { async addNote(note) {
await this.checkVersion(); if (!this._enabled) { return null; }
return await this.ankiInvoke('addNote', {note}); await this._checkVersion();
return await this._invoke('addNote', {note});
} }
async canAddNotes(notes) { async canAddNotes(notes) {
await this.checkVersion(); if (!this._enabled) { return []; }
return await this.ankiInvoke('canAddNotes', {notes}); await this._checkVersion();
return await this._invoke('canAddNotes', {notes});
} }
async getDeckNames() { async getDeckNames() {
await this.checkVersion(); if (!this._enabled) { return []; }
return await this.ankiInvoke('deckNames'); await this._checkVersion();
return await this._invoke('deckNames');
} }
async getModelNames() { async getModelNames() {
await this.checkVersion(); if (!this._enabled) { return []; }
return await this.ankiInvoke('modelNames'); await this._checkVersion();
return await this._invoke('modelNames');
} }
async getModelFieldNames(modelName) { async getModelFieldNames(modelName) {
await this.checkVersion(); if (!this._enabled) { return []; }
return await this.ankiInvoke('modelFieldNames', {modelName}); await this._checkVersion();
return await this._invoke('modelFieldNames', {modelName});
} }
async guiBrowse(query) { async guiBrowse(query) {
await this.checkVersion(); if (!this._enabled) { return []; }
return await this.ankiInvoke('guiBrowse', {query}); await this._checkVersion();
return await this._invoke('guiBrowse', {query});
} }
async storeMediaFile(filename, dataBase64) { async storeMediaFile(filename, dataBase64) {
await this.checkVersion(); if (!this._enabled) {
return await this.ankiInvoke('storeMediaFile', {filename, data: dataBase64}); throw new Error('AnkiConnect not enabled');
}
await this._checkVersion();
return await this._invoke('storeMediaFile', {filename, data: dataBase64});
} }
async checkVersion() { async findNoteIds(notes) {
if (this.remoteVersion < this.localVersion) { if (!this._enabled) { return []; }
this.remoteVersion = await this.ankiInvoke('version'); await this._checkVersion();
if (this.remoteVersion < this.localVersion) { const actions = notes.map((note) => ({
action: 'findNotes',
params: {
query: `deck:"${this._escapeQuery(note.deckName)}" ${this._fieldsToQuery(note.fields)}`
}
}));
return await this._invoke('multi', {actions});
}
// Private
async _checkVersion() {
if (this._remoteVersion < this._localVersion) {
this._remoteVersion = await this._invoke('version');
if (this._remoteVersion < this._localVersion) {
throw new Error('Extension and plugin versions incompatible'); throw new Error('Extension and plugin versions incompatible');
} }
} }
} }
async findNoteIds(notes) { async _invoke(action, params) {
await this.checkVersion(); const result = await requestJson(this._server, 'POST', {action, params, version: this._localVersion});
const actions = notes.map((note) => ({ if (isObject(result)) {
action: 'findNotes', const error = result.error;
params: { if (typeof error !== 'undefined') {
query: `deck:"${AnkiConnect.escapeQuery(note.deckName)}" ${AnkiConnect.fieldsToQuery(note.fields)}` throw new Error(`AnkiConnect error: ${error}`);
} }
})); }
return await this.ankiInvoke('multi', {actions}); return result;
} }
ankiInvoke(action, params) { _escapeQuery(text) {
return requestJson(this.server, 'POST', {action, params, version: this.localVersion});
}
static escapeQuery(text) {
return text.replace(/"/g, ''); return text.replace(/"/g, '');
} }
static fieldsToQuery(fields) { _fieldsToQuery(fields) {
const fieldNames = Object.keys(fields); const fieldNames = Object.keys(fields);
if (fieldNames.length === 0) { if (fieldNames.length === 0) {
return ''; return '';
} }
const key = fieldNames[0]; const key = fieldNames[0];
return `${key.toLowerCase()}:"${AnkiConnect.escapeQuery(fields[key])}"`; return `${key.toLowerCase()}:"${this._escapeQuery(fields[key])}"`;
}
}
/*
* AnkiNull
*/
class AnkiNull {
async addNote() {
return null;
}
async canAddNotes() {
return [];
}
async getDeckNames() {
return [];
}
async getModelNames() {
return [];
}
async getModelFieldNames() {
return [];
}
async guiBrowse() {
return [];
}
async findNoteIds() {
return [];
} }
} }

View File

@ -18,7 +18,6 @@
/* global /* global
* AnkiConnect * AnkiConnect
* AnkiNoteBuilder * AnkiNoteBuilder
* AnkiNull
* AudioSystem * AudioSystem
* AudioUriBuilder * AudioUriBuilder
* BackendApiForwarder * BackendApiForwarder
@ -46,7 +45,7 @@ class Backend {
this.database = new Database(); this.database = new Database();
this.dictionaryImporter = new DictionaryImporter(); this.dictionaryImporter = new DictionaryImporter();
this.translator = new Translator(this.database); this.translator = new Translator(this.database);
this.anki = new AnkiNull(); this.anki = new AnkiConnect();
this.mecab = new Mecab(); this.mecab = new Mecab();
this.clipboardMonitor = new ClipboardMonitor({getClipboard: this._onApiClipboardGet.bind(this)}); this.clipboardMonitor = new ClipboardMonitor({getClipboard: this._onApiClipboardGet.bind(this)});
this.options = null; this.options = null;
@ -55,6 +54,7 @@ class Backend {
this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)}); this.audioSystem = new AudioSystem({getAudioUri: this._getAudioUri.bind(this)});
this.audioUriBuilder = new AudioUriBuilder(); this.audioUriBuilder = new AudioUriBuilder();
this.ankiNoteBuilder = new AnkiNoteBuilder({ this.ankiNoteBuilder = new AnkiNoteBuilder({
anki: this.anki,
audioSystem: this.audioSystem, audioSystem: this.audioSystem,
renderTemplate: this._renderTemplate.bind(this) renderTemplate: this._renderTemplate.bind(this)
}); });
@ -214,7 +214,8 @@ class Backend {
this.setExtensionBadgeText(''); this.setExtensionBadgeText('');
} }
this.anki = options.anki.enable ? new AnkiConnect(options.anki.server) : new AnkiNull(); this.anki.setServer(options.anki.server);
this.anki.setEnabled(options.anki.enable);
if (options.parsing.enableMecabParser) { if (options.parsing.enableMecabParser) {
this.mecab.startListener(); this.mecab.startListener();
@ -505,8 +506,7 @@ class Backend {
await this.ankiNoteBuilder.injectScreenshot( await this.ankiNoteBuilder.injectScreenshot(
definition, definition,
options.anki.terms.fields, options.anki.terms.fields,
details.screenshot, details.screenshot
this.anki
); );
} }