Improve speed of the image calls and fix bug with the generated image links in Anki

This commit is contained in:
reuseman 2020-12-05 23:16:48 +01:00
parent 0d7af53bd8
commit 27064e26d9
4 changed files with 53 additions and 50 deletions

16
main.ts
View File

@ -58,14 +58,14 @@ export default class ObsidianFlashcard extends Plugin {
this.addCommand({
id: "anki-test", name: "Anki", callback: () => {
let anki: Anki = new Anki()
anki.storeMedia(this.app.vault).then(res => {
console.log("ok")
console.log(res)
}).catch(err => {
console.log("err")
console.log(err)
})
// let anki: Anki = new Anki()
// anki.storeMedia(this.app.vault).then(res => {
// console.log("ok")
// console.log(res)
// }).catch(err => {
// console.log("err")
// console.log(err)
// })
}
})

View File

@ -4,6 +4,7 @@ import { Parser } from 'src/parser'
import { Settings } from 'src/settings'
import { Card } from 'src/entities/card'
import { Flashcard } from 'src/entities/flashcard'
import { arrayBufferToBase64 } from "src/utils"
export class CardsService {
// TODO right now you do not check for cards that when inserted/updated gives back null as ID
@ -27,7 +28,6 @@ export class CardsService {
public async execute(activeFile: TFile): Promise<string[]> {
// TODO add note-type to Anki
// TODO check media problem
try {
await this.anki.ping()
} catch (err) {
@ -60,16 +60,7 @@ export class CardsService {
let [cardsToCreate, cardsToUpdate] = this.filterByUpdate(ankiCards, cards)
let cardsToDelete: number[] = this.parser.getCardsToDelete(this.file)
try {
// Currently the media are created for every run, this is not a problem since Anki APIs overwrite the file
// A more efficient way would be to keep track of the medias saved
await this.generateMediaLinks(cards)
await this.anki.storeMediaFiles(cards)
} catch (err) {
console.error(err)
Error("Error: Could not upload medias")
}
this.insertMedias(cards)
await this.deleteCardsOnAnki(cardsToDelete, ankiBlocks)
await this.updateCardsOnAnki(cardsToUpdate)
await this.insertCardsOnAnki(cardsToCreate, frontmatter, deckName)
@ -94,14 +85,16 @@ export class CardsService {
}
}
private arrayBufferToBase64(buffer: ArrayBuffer): string {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
private async insertMedias(cards: Card[]) {
try {
// Currently the media are created for every run, this is not a problem since Anki APIs overwrite the file
// A more efficient way would be to keep track of the medias saved
await this.generateMediaLinks(cards)
await this.anki.storeMediaFiles(cards)
} catch (err) {
console.error(err)
Error("Error: Could not upload medias")
}
return window.btoa(binary);
}
private async generateMediaLinks(cards: Card[]) {
@ -114,7 +107,7 @@ export class CardsService {
let file: TFile = this.app.vault.getAbstractFileByPath(attachmentsPath + "/" + media) as TFile
try {
let binaryMedia = await this.app.vault.readBinary(file)
card.mediaBase64Encoded.push(this.arrayBufferToBase64(binaryMedia))
card.mediaBase64Encoded.push(arrayBufferToBase64(binaryMedia))
} catch (err) {
Error("Error: Could not read media")
}
@ -231,7 +224,6 @@ export class CardsService {
private getAnkiIDs(blocks: RegExpMatchArray[]): number[] {
let IDs: number[] = []
for (let b of blocks) {
IDs.push(Number(b[1]))
}
@ -260,7 +252,6 @@ export class CardsService {
}
}
} else {
console.log("No cards in Anki, I am going to create all of them")
cardsToCreate = [...generatedCards]
}

View File

@ -3,7 +3,7 @@ import { Spaced } from "./entities/spaced"
import { Cloze } from './entities/cloze';
import { Settings } from 'src/settings';
import * as showdown from 'showdown';
import { wikiImageLinks, markdownImageLinks } from "src/utils";
export class Parser {
private settings: Settings
@ -65,7 +65,7 @@ export class Parser {
public getCardsToDelete(file: string): number[] {
// Find block IDs with no content above it
let regex: RegExp = /^\s*(?:\n)(?:\^(\d{13}))(?:\n\s*?)?/gm
const regex: RegExp = /^\s*(?:\n)(?:\^(\d{13}))(?:\n\s*?)?/gm
return [...file.matchAll(regex)].map((match) => { return Number(match[1]) })
}
@ -79,24 +79,26 @@ export class Parser {
if (contextAware) {
// https://regex101.com/r/agSp9X/4
headings = [...file.matchAll(/^ {0,3}(#{1,6}) +([^\n]+?) ?((?: *#\S+)*) *$/gim)]
const headingsRegex = /^ {0,3}(#{1,6}) +([^\n]+?) ?((?: *#\S+)*) *$/gim
headings = [...file.matchAll(headingsRegex)]
}
for (let match of matches) {
let reversed: boolean = match[3].trim().toLowerCase() === "#flashcard-reverse"
let headingLevel = match[1].trim().length !== 0 ? match[1].length : -1
// Match.index - 1 because otherwise in the context there will be even match[1], i.e. the question
// Match.index - 1 because otherwise in the context there will be even match[1], i.e. the question itself
let context = contextAware ? this.getContext(headings, match.index - 1, headingLevel) : ""
let originalQuestion = match[2].trim()
let question = contextAware ? [...context, match[2].trim()].join(`${this.settings.contextSeparator}`) : match[2].trim()
let answer = match[5].trim()
let imagesMedia: string[] = this.substituteImageLinks(question)
imagesMedia = imagesMedia.concat(this.substituteImageLinks(answer))
let imagesMedia: string[] = this.getImageLinks(question)
imagesMedia = imagesMedia.concat(this.getImageLinks(answer))
question = this.substituteImageLinks(question)
answer = this.substituteImageLinks(answer)
question = this.mathToAnki(this.htmlConverter.makeHtml(question))
answer = this.mathToAnki(this.htmlConverter.makeHtml(answer))
let endingLine = match.index + match[0].length
let tags: string[] = this.parseTags(match[4], globalTags)
let id: number = match[6] ? Number(match[6]) : -1
@ -110,13 +112,9 @@ export class Parser {
return flashcards
}
private substituteImageLinks(str: string): string[] {
// Supported images https://publish.obsidian.md/help/How+to/Embed+files
let wikiLinks = /!\[\[(.*\.(?:png|jpg|jpeg|gif|bmp|svg|tiff))\]\]/gim
let markdownLinks = /!\[\]\((.*\.(?:png|jpg|jpeg|gif|bmp|svg|tiff))\)/gim
let wikiMatches = str.matchAll(wikiLinks)
let markdownMatches = str.matchAll(markdownLinks)
private getImageLinks(str: string) {
let wikiMatches = str.matchAll(wikiImageLinks)
let markdownMatches = str.matchAll(markdownImageLinks)
let links: string[] = []
for (let wikiMatch of wikiMatches) {
@ -127,15 +125,16 @@ export class Parser {
links.push(decodeURIComponent(markdownMatch[1]))
}
str.replace(wikiLinks, "<img src='$1'>")
// TODO markdown link maybe is not correct, it should be decoded (remove the 20%)
str.replace(markdownLinks, "<img src='$1'>")
console.log("Generated links:")
console.log(links)
return links
}
private substituteImageLinks(str: string): string {
str = str.replace(wikiImageLinks, "<img src='$1'>")
str = str.replace(markdownImageLinks, "<img src='$1'>")
return str
}
private generateSpacedCards(file: string): Spaced[] {
let spaced: Spaced[] = []

13
src/utils.ts Normal file
View File

@ -0,0 +1,13 @@
// Supported images https://publish.obsidian.md/help/How+to/Embed+files
export const wikiImageLinks = /!\[\[(.*\.(?:png|jpg|jpeg|gif|bmp|svg|tiff))\]\]/gim
export const markdownImageLinks = /!\[\]\((.*\.(?:png|jpg|jpeg|gif|bmp|svg|tiff))\)/gim
export function arrayBufferToBase64(buffer: ArrayBuffer): string {
var binary = '';
var bytes = new Uint8Array(buffer);
var len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}