Skip to content

Instantly share code, notes, and snippets.

@dylan-sessler
Created September 26, 2022 20:53
Show Gist options
  • Save dylan-sessler/1722cd17689ae57b6da9b41c952b881f to your computer and use it in GitHub Desktop.
Save dylan-sessler/1722cd17689ae57b6da9b41c952b881f to your computer and use it in GitHub Desktop.
anki stuff
const axios = require('axios')
const childProcess = require('child_process')
const path = require('path')
module.exports = {openReviewGui, getDeckStats}
async function anki(method, url, dataObject){
// implemented using the ankiConnect plugin
// https://foosoft.net/projects/anki-connect/
// https://ankiweb.net/shared/info/2055492159
if(!isAnkiRunning()){
startAnkiDetached()
waitForAnkiToBootUp()
}
const result = ankiRequest(method, url, dataObject)
.then(parseAnkiResponse)
.catch((error) => {
if(error.code === 'ECONNREFUSED'){
throw Error('Unable to connect to the anki connect server. Do you have the Anki Connect add-on enabled?')
}
else {throw error}
})
return result
}
function ankiRequest(method, url, dataObject){
return axios({
method: method,
url: url,
data: dataObject,
validateStatus: () => true, // every status code is a success, handle status codes differently in the parser. Default is 200-299 is success, rest is error
})
}
function parseAnkiResponse(response) {
switch(response.status) {
case 200:
if(response.data){
return response.data
} else {
const responseDataFormatError = Error('response.data was empty')
throw responseDataFormatError
}
break
default:
const unhandledStatusCodeError = Error(`Unhandled status code ${response.status}`)
unhandledStatusCodeError.responseData = response.data
throw unhandledStatusCodeError
}
}
async function openReviewGui(deckName) {
const dataObj = {
action: "guiDeckReview",
version: 6,
params: {name: deckName}
}
const result = await anki('post', 'http://localhost:8765', dataObj)
if(result.result === null){
throw Error('Anki deck was not found when trying to open review. Have you entered it yet?')
}
raiseAnki()
}
async function getDeckStats(deckName) {
// If the deckName does not exist in anki, it will be created. This is some
// ankiConnect weirdness.
// TODO determine if a new deck was created (will have a key on the result
// that is not `1`, and is the deck Id (eg. 165468746)) and when it is throw
// an error and delete the new deck.
const dataObj = {
action: "getDeckStats",
version: 6,
params: {decks: [deckName]}
}
const result = await anki('post', 'http://localhost:8765', dataObj)
const deckStats = result.result[1]
return deckStats
}
// Fxns that do bash stuff to do a variety of things
function isAnkiRunning() {
const pathToFile = path.join(__dirname, 'bash-scripts', 'is-anki-running.sh')
const isAnkiRunningExitCode = childProcess.execFileSync(pathToFile).toString()
if(Number(isAnkiRunningExitCode) === 0){return true}
else {return false}
}
function startAnkiDetached() {
// const out = fs.openSync('./out.log', 'a') // only needed if you want to see the logs
// const err = fs.openSync('./out.log', 'a') // only needed if you want to see the logs
childProcess.spawn('anki', [], {
// https://stackoverflow.com/questions/25323703/nodejs-execute-command-in-background-and-forget
// By default parent process will wait for the detached child to exit, and
// it'll also listen for its stdio. To completely detach child process from the
// parent you should:
stdio: 'ignore', // detaches child's stdio from parent by piping all stdio to /dev/null
// stdio: ['ignore', out, err], // detaches stdio from parent by piping all stdio to a log file so you can check in on it
detached: true // makes the child the leader of a new process group so it will keep running after the parent exits
}).unref() // remove child process from the parent event loop reference count using unref() method
}
function waitForAnkiToBootUp() {
const pathToFile = path.join(__dirname, 'bash-scripts', 'wait-for-anki.sh')
const waitForAnki = childProcess.execFileSync(pathToFile,[],{timeout: 10000})
}
function raiseAnki() {
const pathToFile = path.join(__dirname, 'bash-scripts', 'raise-anki.sh')
const stdout = childProcess.execFileSync(pathToFile).toString()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment