Anki screenshot refactor (#791)
* Use more consistent style for injectScreenshot * Move screenshot generation into AnkiNoteBuilder/Backend * Get optionsContext before await
This commit is contained in:
parent
2aa86cc5f8
commit
e3a7678769
@ -20,11 +20,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class AnkiNoteBuilder {
|
class AnkiNoteBuilder {
|
||||||
constructor({anki, audioSystem, renderTemplate, getClipboardImage=null}) {
|
constructor({anki, audioSystem, renderTemplate, getClipboardImage=null, getScreenshot=null}) {
|
||||||
this._anki = anki;
|
this._anki = anki;
|
||||||
this._audioSystem = audioSystem;
|
this._audioSystem = audioSystem;
|
||||||
this._renderTemplate = renderTemplate;
|
this._renderTemplate = renderTemplate;
|
||||||
this._getClipboardImage = getClipboardImage;
|
this._getClipboardImage = getClipboardImage;
|
||||||
|
this._getScreenshot = getScreenshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createNote({
|
async createNote({
|
||||||
@ -130,18 +131,23 @@ class AnkiNoteBuilder {
|
|||||||
async injectScreenshot(definition, fields, screenshot) {
|
async injectScreenshot(definition, fields, screenshot) {
|
||||||
if (!this._containsMarker(fields, 'screenshot')) { return; }
|
if (!this._containsMarker(fields, 'screenshot')) { return; }
|
||||||
|
|
||||||
|
const reading = definition.reading;
|
||||||
const now = new Date(Date.now());
|
const now = new Date(Date.now());
|
||||||
let fileName = `yomichan_browser_screenshot_${definition.reading}_${this._dateToString(now)}.${screenshot.format}`;
|
|
||||||
fileName = AnkiNoteBuilder.replaceInvalidFileNameCharacters(fileName);
|
|
||||||
const data = screenshot.dataUrl.replace(/^data:[\w\W]*?,/, '');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const {windowId, tabId, ownerFrameId, format, quality} = screenshot;
|
||||||
|
const dataUrl = await this._getScreenshot(windowId, tabId, ownerFrameId, format, quality);
|
||||||
|
|
||||||
|
let fileName = `yomichan_browser_screenshot_${reading}_${this._dateToString(now)}.${format}`;
|
||||||
|
fileName = AnkiNoteBuilder.replaceInvalidFileNameCharacters(fileName);
|
||||||
|
const data = dataUrl.replace(/^data:[\w\W]*?,/, '');
|
||||||
|
|
||||||
await this._anki.storeMediaFile(fileName, data);
|
await this._anki.storeMediaFile(fileName, data);
|
||||||
} catch (e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
definition.screenshotFileName = fileName;
|
definition.screenshotFileName = fileName;
|
||||||
|
} catch (e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async injectClipboardImage(definition, fields) {
|
async injectClipboardImage(definition, fields) {
|
||||||
|
@ -61,7 +61,8 @@ class Backend {
|
|||||||
anki: this._anki,
|
anki: this._anki,
|
||||||
audioSystem: this._audioSystem,
|
audioSystem: this._audioSystem,
|
||||||
renderTemplate: this._renderTemplate.bind(this),
|
renderTemplate: this._renderTemplate.bind(this),
|
||||||
getClipboardImage: this._onApiClipboardImageGet.bind(this)
|
getClipboardImage: this._onApiClipboardImageGet.bind(this),
|
||||||
|
getScreenshot: this._getScreenshot.bind(this)
|
||||||
});
|
});
|
||||||
this._templateRenderer = new TemplateRenderer();
|
this._templateRenderer = new TemplateRenderer();
|
||||||
|
|
||||||
@ -442,7 +443,7 @@ class Backend {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onApiDefinitionAdd({definition, mode, context, details, optionsContext}) {
|
async _onApiDefinitionAdd({definition, mode, context, ownerFrameId, optionsContext}, sender) {
|
||||||
const options = this.getOptions(optionsContext);
|
const options = this.getOptions(optionsContext);
|
||||||
const templates = this._getTemplates(options);
|
const templates = this._getTemplates(options);
|
||||||
const fields = (
|
const fields = (
|
||||||
@ -463,13 +464,13 @@ class Backend {
|
|||||||
|
|
||||||
await this._ankiNoteBuilder.injectClipboardImage(definition, fields);
|
await this._ankiNoteBuilder.injectClipboardImage(definition, fields);
|
||||||
|
|
||||||
if (details && details.screenshot) {
|
const {id: tabId, windowId} = (sender && sender.tab ? sender.tab : {});
|
||||||
|
const {format, quality} = options.anki.screenshot;
|
||||||
await this._ankiNoteBuilder.injectScreenshot(
|
await this._ankiNoteBuilder.injectScreenshot(
|
||||||
definition,
|
definition,
|
||||||
fields,
|
fields,
|
||||||
details.screenshot
|
{windowId, tabId, ownerFrameId, format, quality}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
const note = await this._createNote(definition, mode, context, options, templates);
|
const note = await this._createNote(definition, mode, context, options, templates);
|
||||||
return this._anki.addNote(note);
|
return this._anki.addNote(note);
|
||||||
@ -1626,4 +1627,40 @@ class Backend {
|
|||||||
modeOptions
|
modeOptions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _getScreenshot(windowId, tabId, ownerFrameId, format, quality) {
|
||||||
|
if (typeof windowId !== 'number') {
|
||||||
|
throw new Error('Invalid window ID');
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = null;
|
||||||
|
try {
|
||||||
|
if (typeof tabId === 'number' && typeof ownerFrameId === 'number') {
|
||||||
|
const action = 'setAllVisibleOverride';
|
||||||
|
const params = {value: false, priority: 0, awaitFrame: true};
|
||||||
|
token = await this._sendMessageTab(tabId, {action, params}, {frameId: ownerFrameId});
|
||||||
|
}
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
chrome.tabs.captureVisibleTab(windowId, {format, quality}, (result) => {
|
||||||
|
const e = chrome.runtime.lastError;
|
||||||
|
if (e) {
|
||||||
|
reject(new Error(e.message));
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (token !== null) {
|
||||||
|
const action = 'clearAllVisibleOverride';
|
||||||
|
const params = {token};
|
||||||
|
try {
|
||||||
|
await this._sendMessageTab(tabId, {action, params}, {frameId: ownerFrameId});
|
||||||
|
} catch (e) {
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,9 @@ class Frontend {
|
|||||||
this._isPointerOverPopup = false;
|
this._isPointerOverPopup = false;
|
||||||
|
|
||||||
this._runtimeMessageHandlers = new Map([
|
this._runtimeMessageHandlers = new Map([
|
||||||
['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}]
|
['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}],
|
||||||
|
['setAllVisibleOverride', {async: true, handler: this._onApiSetAllVisibleOverride.bind(this)}],
|
||||||
|
['clearAllVisibleOverride', {async: true, handler: this._onApiClearAllVisibleOverride.bind(this)}]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,9 +119,7 @@ class Frontend {
|
|||||||
['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
|
['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
|
||||||
['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
|
['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
|
||||||
['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}],
|
['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}],
|
||||||
['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}],
|
['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}]
|
||||||
['setAllVisibleOverride', {async: true, handler: this._onApiSetAllVisibleOverride.bind(this)}],
|
|
||||||
['clearAllVisibleOverride', {async: true, handler: this._onApiClearAllVisibleOverride.bind(this)}]
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this._updateContentScale();
|
this._updateContentScale();
|
||||||
|
@ -77,8 +77,8 @@ const api = (() => {
|
|||||||
return this._invoke('kanjiFind', {text, optionsContext});
|
return this._invoke('kanjiFind', {text, optionsContext});
|
||||||
}
|
}
|
||||||
|
|
||||||
definitionAdd(definition, mode, context, details, optionsContext) {
|
definitionAdd(definition, mode, context, ownerFrameId, optionsContext) {
|
||||||
return this._invoke('definitionAdd', {definition, mode, context, details, optionsContext});
|
return this._invoke('definitionAdd', {definition, mode, context, ownerFrameId, optionsContext});
|
||||||
}
|
}
|
||||||
|
|
||||||
definitionsAddable(definitions, modes, context, optionsContext) {
|
definitionsAddable(definitions, modes, context, optionsContext) {
|
||||||
|
@ -1047,18 +1047,10 @@ class Display extends EventDispatcher {
|
|||||||
try {
|
try {
|
||||||
this.setSpinnerVisible(true);
|
this.setSpinnerVisible(true);
|
||||||
|
|
||||||
const details = {};
|
const ownerFrameId = this._ownerFrameId;
|
||||||
if (this._noteUsesScreenshot(mode)) {
|
const optionsContext = this.getOptionsContext();
|
||||||
try {
|
|
||||||
const screenshot = await this._getScreenshot();
|
|
||||||
details.screenshot = screenshot;
|
|
||||||
} catch (e) {
|
|
||||||
// NOP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const noteContext = await this._getNoteContext();
|
const noteContext = await this._getNoteContext();
|
||||||
const noteId = await api.definitionAdd(definition, mode, noteContext, details, this.getOptionsContext());
|
const noteId = await api.definitionAdd(definition, mode, noteContext, ownerFrameId, optionsContext);
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
const index = this._definitions.indexOf(definition);
|
const index = this._definitions.indexOf(definition);
|
||||||
const adderButton = this._adderButtonFind(index, mode);
|
const adderButton = this._adderButtonFind(index, mode);
|
||||||
@ -1136,36 +1128,6 @@ class Display extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_noteUsesScreenshot(mode) {
|
|
||||||
const optionsAnki = this._options.anki;
|
|
||||||
const fields = (mode === 'kanji' ? optionsAnki.kanji : optionsAnki.terms).fields;
|
|
||||||
for (const fieldValue of Object.values(fields)) {
|
|
||||||
if (fieldValue.includes('{screenshot}')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async _getScreenshot() {
|
|
||||||
const ownerFrameId = this._ownerFrameId;
|
|
||||||
let token = null;
|
|
||||||
try {
|
|
||||||
if (ownerFrameId !== null) {
|
|
||||||
token = await api.crossFrame.invoke(ownerFrameId, 'setAllVisibleOverride', {value: false, priority: 0, awaitFrame: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
const {format, quality} = this._options.anki.screenshot;
|
|
||||||
const dataUrl = await api.screenshotGet({format, quality});
|
|
||||||
|
|
||||||
return {dataUrl, format};
|
|
||||||
} finally {
|
|
||||||
if (token !== null) {
|
|
||||||
await api.crossFrame.invoke(ownerFrameId, 'clearAllVisibleOverride', {token});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getFirstExpressionIndex() {
|
_getFirstExpressionIndex() {
|
||||||
return this._options.general.resultOutputMode === 'merge' ? 0 : -1;
|
return this._options.general.resultOutputMode === 'merge' ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user