anki-connect/README.md

781 lines
18 KiB
Markdown
Raw Normal View History

2016-09-07 04:15:55 +00:00
# AnkiConnect #
2016-05-30 02:59:21 +00:00
2017-04-01 20:21:04 +00:00
The AnkiConnect plugin enables external applications such as [Yomichan](https://foosoft.net/projects/yomichan/) to communicate with
[Anki](https://apps.ankiweb.net/) over a network interface. This software makes it possible to execute queries against
the user's card deck, automatically create new vocabulary and Kanji flash cards, and more. AnkiConnect is compatible
with the latest stable (2.0.x) and alpha (2.1.x) releases of Anki and works on Linux, Windows, and Mac OS X.
2016-05-30 02:59:21 +00:00
2017-04-01 20:21:04 +00:00
## Installation ##
2016-05-30 02:59:21 +00:00
2017-03-15 04:10:45 +00:00
The installation process is similar to that of other Anki plugins and can be accomplished in three steps:
1. Open the *Install Add-on* dialog by selecting *Tools* > *Add-ons* > *Browse & Install* in Anki.
2017-04-01 20:21:04 +00:00
2. Input *[2055492159](https://ankiweb.net/shared/info/2055492159)* into the text box labeled *Code* and press the *OK* button to proceed.
2017-03-15 04:10:45 +00:00
3. Restart Anki when prompted to do so in order to complete the installation of AnkiConnect.
2017-04-01 20:21:04 +00:00
Anki must be kept running in the background in order for other applications to be able to use AnkiConnect. You can
verify that AnkiConnect is running at any time by accessing [localhost:8765](http://localhost:8765) in your browser. If
2017-05-28 22:54:28 +00:00
the server is running, you should see the message *AnkiConnect v.3* displayed in your browser window.
2016-07-03 02:46:01 +00:00
2017-04-22 06:36:47 +00:00
### Notes for Windows Users ###
2017-01-28 20:20:38 +00:00
2017-04-01 20:21:04 +00:00
Windows users may see a firewall nag dialog box appear on Anki startup. This occurs because AnkiConnect hosts a local
server in order to enable other applications to connect to it. The host application, Anki, must be unblocked for this
plugin to function correctly.
2017-01-28 20:20:38 +00:00
2017-04-22 06:36:47 +00:00
### Notes for Mac OS X Users ###
2017-01-28 20:20:38 +00:00
2017-02-12 00:41:02 +00:00
Starting with [Mac OS X Mavericks](https://en.wikipedia.org/wiki/OS_X_Mavericks), a feature named *App Nap* has been
introduced to the operating system. This feature causes certain applications which are open (but not visible) to be
2017-02-12 03:26:22 +00:00
placed in a suspended state. As this behavior causes AnkiConnect to stop working while you have another window in the
2017-02-12 00:41:02 +00:00
foreground, App Nap should be disabled for Anki:
1. Start the Terminal application.
2. Execute the following command in the terminal window:
```
defaults write net.ichi2.anki NSAppSleepDisabled -bool true
```
3. Restart Anki.
2017-04-22 06:36:47 +00:00
## Application Interface for Developers ##
2017-02-01 17:23:09 +00:00
2017-02-03 04:14:24 +00:00
AnkiConnect exposes Anki features to external applications via an easy to use
[RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) API. After it is installed, this plugin will
initialize a minimal HTTP sever running on port 8765 every time Anki executes. Other applications (including browser
extensions) can then communicate with it via HTTP POST requests.
2017-02-01 17:23:09 +00:00
2017-02-03 04:14:24 +00:00
### Sample Invocation ###
2017-02-01 17:23:09 +00:00
2017-05-06 19:17:15 +00:00
Every request consists of a JSON-encoded object containing an *action*, and a set of contextual *parameters*. A simple
example of a JavaScript application communicating with the extension is illustrated below:
2017-02-01 17:23:09 +00:00
```JavaScript
2017-02-03 04:14:24 +00:00
function ankiInvoke(action, params={}) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.addEventListener('loadend', () => {
if (xhr.responseText) {
resolve(JSON.parse(xhr.responseText));
} else {
reject('unable to connect to plugin');
}
});
xhr.open('POST', 'http://127.0.0.1:8765');
xhr.send(JSON.stringify({action, params}));
});
}
2017-02-01 17:23:09 +00:00
2017-02-03 04:14:24 +00:00
ankiInvoke('version').then(response => {
window.alert(`detected API version: ${response}`);
}).catch(error => {
window.alert(`could not get API version: ${error}`);
});
2017-02-01 17:23:09 +00:00
```
2017-05-06 12:39:38 +00:00
Or using [`curl`](https://curl.haxx.se):
```
2017-07-02 00:29:48 +00:00
curl localhost:8765 -X POST -d '{"action": "version"}'
2017-05-06 12:39:38 +00:00
```
2017-02-03 04:14:24 +00:00
### Supported Actions ###
2017-02-01 17:23:09 +00:00
2017-07-02 00:29:48 +00:00
Below is a list of currently supported actions. Requests with invalid actions or parameters will a return `null` result.
2017-02-01 17:23:09 +00:00
2017-04-22 06:36:47 +00:00
* **version**
2017-02-01 17:23:09 +00:00
2017-07-01 20:16:51 +00:00
Gets the version of the API exposed by this plugin. Currently versions `1` through `4` are defined.
2017-02-03 04:14:24 +00:00
This should be the first call you make to make sure that your application and AnkiConnect are able to communicate
2017-08-19 18:38:55 +00:00
properly with each other. New versions of AnkiConnect will backwards compatible; as long as you are using actions
2017-02-03 04:14:24 +00:00
which are available in the reported AnkiConnect version or earlier, everything should work fine.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:29:48 +00:00
"action": "version"
2017-02-01 17:23:09 +00:00
}
2017-02-03 04:14:24 +00:00
```
2017-02-01 17:23:09 +00:00
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
2017-07-02 00:29:48 +00:00
4
2017-02-03 04:14:24 +00:00
```
* **multi**
Performs multiple actions in one request, returning an array with the response of each action (in the given order).
*Sample request*:
```
{
"action": "multi",
"params": {
"actions": [
{"action": "deckNames"},
{
"action": "browse",
"params": {"query": "deck:current"}
}
]
}
}
```
*Sample response*:
```
[
["Default"],
[1494723142483, 1494703460437, 1494703479525]
]
```
2017-04-22 06:36:47 +00:00
* **deckNames**
2017-02-03 04:14:24 +00:00
Gets the complete list of deck names for the current user.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:29:48 +00:00
"action": "deckNames"
2017-02-03 04:14:24 +00:00
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
[
2017-07-02 00:12:11 +00:00
"Default"
2017-02-03 04:14:24 +00:00
]
```
2017-04-22 06:36:47 +00:00
* **modelNames**
2017-02-03 04:14:24 +00:00
Gets the complete list of model names for the current user.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:29:48 +00:00
"action": "modelNames"
2017-02-03 04:14:24 +00:00
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
[
2017-07-02 00:12:11 +00:00
"Basic",
"Basic (and reversed card)"
2017-02-03 04:14:24 +00:00
]
```
2017-04-22 06:36:47 +00:00
* **modelFieldNames**
2017-02-03 04:14:24 +00:00
Gets the complete list of field names for the provided model name.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:12:11 +00:00
"action": "modelFieldNames",
"params": {
"modelName": "Basic"
2017-02-01 17:23:09 +00:00
}
}
2017-02-03 04:14:24 +00:00
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
[
2017-07-02 00:12:11 +00:00
"Front",
"Back"
2017-02-03 04:14:24 +00:00
]
```
2017-08-18 19:57:19 +00:00
* **getDeckConfig**
2017-07-27 17:54:02 +00:00
Gets the config group object for the given deck.
2017-07-27 17:54:02 +00:00
*Sample request*:
```
{
2017-08-18 19:57:19 +00:00
"action": "getDeckConfig",
2017-07-27 17:54:02 +00:00
"params": {
"deck": "Default"
2017-07-27 17:54:02 +00:00
}
}
```
*Sample response*:
```
{
"lapse": {
"leechFails": 8,
"delays": [10],
"minInt": 1,
"leechAction": 0,
"mult": 0
},
"dyn": false,
"autoplay": true,
"mod": 1502970872,
"id": 1,
"maxTaken": 60,
"new": {
"bury": true,
"order": 1,
"initialFactor": 2500,
"perDay": 20,
"delays": [1, 10],
"separate": true,
"ints": [1, 4, 7]
},
"name": "Default",
"rev": {
"bury": true,
"ivlFct": 1,
"ease4": 1.3,
"maxIvl": 36500,
"perDay": 100,
"minSpace": 1,
"fuzz": 0.05
},
"timer": 0,
"replayq": true,
"usn": -1
}
```
2017-08-18 19:57:19 +00:00
* **saveDeckConfig**
Saves the given config group, returning `true` on success or `false` if the ID of the config group is invalid (i.e.
it does not exist).
*Sample request*:
```
{
2017-08-18 19:57:19 +00:00
"action": "saveDeckConfig",
"params": {
2017-08-18 20:16:50 +00:00
"config": (config group object)
}
}
```
*Sample response*:
```
true
```
2017-08-18 19:57:19 +00:00
* **setDeckConfigId**
Changes the configuration group for the given decks to the one with the given ID. Returns `true` on success or
`false` if the given configuration group or any of the given decks do not exist.
*Sample request*:
```
{
2017-08-18 19:57:19 +00:00
"action": "setDeckConfigId",
"params": {
"decks": ["Default"],
2017-08-18 20:10:51 +00:00
"configId": 1
}
}
```
*Sample response*:
```
true
```
2017-08-18 19:57:19 +00:00
* **cloneDeckConfigId**
Creates a new config group with the given name, cloning from the group with the given ID, or from the default group
if this is unspecified. Returns the ID of the new config group, or `false` if the specified group to clone from does
not exist.
*Sample request*:
```
{
2017-08-18 19:57:19 +00:00
"action": "cloneDeckConfigId",
"params": {
"name": "Copy of Default",
"cloneFrom": 1
}
}
```
*Sample response*:
```
1502972374573
```
2017-08-18 19:57:19 +00:00
* **removeDeckConfigId**
Removes the config group with the given ID, returning `true` if successful, or `false` if attempting to remove
either the default config group (ID = 1) or a config group that does not exist.
*Sample request*:
```
{
2017-08-18 19:57:19 +00:00
"action": "removeDeckConfigId",
"params": {
2017-08-18 20:10:51 +00:00
"configId": 1502972374573
}
}
```
*Sample response*:
```
true
2017-07-27 17:54:02 +00:00
```
2017-04-22 06:36:47 +00:00
* **addNote**
2017-02-03 04:14:24 +00:00
2017-02-05 20:08:52 +00:00
Creates a note using the given deck and model, with the provided field values and tags. Returns the identifier of
the created note created on success, and `null` on failure.
2017-02-03 04:14:24 +00:00
AnkiConnect can download audio files and embed them in newly created notes. The corresponding *audio* note member is
optional and can be omitted. If you choose to include it, the *url* and *filename* fields must be also defined. The
*skipHash* field can be optionally provided to skip the inclusion of downloaded files with an MD5 hash that matches
the provided value. This is useful for avoiding the saving of error pages and stub files.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:12:11 +00:00
"action": "addNote",
"params": {
"note": {
"deckName": "Default",
"modelName": "Basic",
"fields": {
"Front": "front content",
"Back": "back content",
2017-05-31 02:48:24 +00:00
},
2017-07-02 00:12:11 +00:00
"tags": [
"yomichan",
2017-05-31 02:48:24 +00:00
],
2017-07-02 00:12:11 +00:00
"audio": {
"url": "https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?kanji=猫&kana=ねこ",
"filename": "yomichan_ねこ_猫.mp3",
"skipHash": "7e2c2f954ef6051373ba916f000168dc"
2017-05-31 02:48:24 +00:00
}
2017-02-03 04:14:24 +00:00
}
}
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
2017-05-31 02:48:24 +00:00
1496198395707
2017-02-03 04:14:24 +00:00
```
2017-02-05 20:08:52 +00:00
2017-04-22 06:36:47 +00:00
* **addNotes**
2017-02-05 20:08:52 +00:00
Creates multiple notes using the given deck and model, with the provided field values and tags. Returns an array of
identifiers of the created notes (notes that could not be created will have a `null` identifier). Please see the
documentation for `addNote` for an explanation of objects in the `notes` array.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-05 20:08:52 +00:00
```
{
2017-07-02 00:12:11 +00:00
"action": "addNotes",
"params": {
"notes": [
2017-02-05 20:08:52 +00:00
{
2017-07-02 00:12:11 +00:00
"deckName": "Default",
"modelName": "Basic",
"fields": {
"Front": "front content",
"Back": "back content"
2017-02-05 20:08:52 +00:00
},
2017-07-02 00:12:11 +00:00
"tags": [
"yomichan"
2017-02-05 20:08:52 +00:00
],
2017-07-02 00:12:11 +00:00
"audio": {
"url": "https://assets.languagepod101.com/dictionary/japanese/audiomp3.php?kanji=猫&kana=ねこ",
"filename": "yomichan_ねこ_猫.mp3",
"skipHash": "7e2c2f954ef6051373ba916f000168dc"
2017-02-05 20:08:52 +00:00
}
2017-07-02 00:12:11 +00:00
}
2017-02-05 20:08:52 +00:00
]
}
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-05 20:08:52 +00:00
```
[
2017-05-31 02:48:24 +00:00
1496198395707,
2017-07-02 00:12:11 +00:00
null
2017-02-05 20:08:52 +00:00
]
```
2017-04-22 06:36:47 +00:00
* **canAddNotes**
2017-02-03 04:14:24 +00:00
Accepts an array of objects which define parameters for candidate notes (see `addNote`) and returns an array of
booleans indicating whether or not the parameters at the corresponding index could be used to create a new note.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-03 04:14:24 +00:00
```
{
2017-07-02 00:12:11 +00:00
"action": "canAddNotes",
"params": {
"notes": [
2017-02-05 20:08:52 +00:00
{
2017-07-02 00:12:11 +00:00
"deckName": "Default",
"modelName": "Basic",
"fields": {
"Front": "front content",
"Back": "back content"
2017-02-03 04:14:24 +00:00
},
2017-07-02 00:12:11 +00:00
"tags": [
"yomichan"
2017-02-03 04:14:24 +00:00
]
2017-07-02 00:12:11 +00:00
}
2017-02-03 04:14:24 +00:00
]
}
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-03 04:14:24 +00:00
```
[
2017-07-02 00:12:11 +00:00
true
2017-02-03 04:14:24 +00:00
]
```
2017-02-01 17:23:09 +00:00
2017-08-03 20:07:22 +00:00
* **addTags**
Adds tags to notes by note ID.
2017-08-03 20:07:22 +00:00
*Sample request*:
```
{
"action": "addTags",
"params": {
"notes": [1483959289817, 1483959291695],
2017-08-03 20:07:22 +00:00
"tags": "european-languages"
}
}
```
*Sample response*:
```
null
```
* **removeTags**
Remove tags from notes by note ID.
2017-08-03 20:07:22 +00:00
*Sample request*:
```
{
"action": "removeTags",
"params": {
"notes": [1483959289817, 1483959291695],
2017-08-03 20:07:22 +00:00
"tags": "european-languages"
}
}
```
*Sample response*:
```
null
```
* **suspend**
2017-08-19 18:38:55 +00:00
Suspend cards by card ID.
2017-08-03 20:07:22 +00:00
*Sample request*:
```
{
"action": "suspend",
"params": {
"cards": [1483959291685, 1483959293217]
2017-08-03 20:07:22 +00:00
}
}
```
*Sample response*:
```
2017-08-19 18:38:55 +00:00
null
2017-08-03 20:07:22 +00:00
```
* **unsuspend**
2017-08-19 18:38:55 +00:00
Unsuspend cards by card ID.
2017-08-03 20:07:22 +00:00
*Sample request*:
```
{
"action": "unsuspend",
"params": {
"cards": [1483959291685, 1483959293217]
2017-08-03 20:07:22 +00:00
}
}
```
*Sample response*:
```
2017-08-19 18:38:55 +00:00
null
```
* **findNotes**
2017-05-28 20:22:21 +00:00
Returns an array of note IDs for a given query (same query syntax as **guiBrowse**).
2017-05-28 20:22:21 +00:00
*Sample request*:
```
{
2017-08-19 18:38:55 +00:00
"action": "findCards",
"params": {
"query": "deck:current"
}
}
```
*Sample response*:
```
[
1483959289817,
1483959291695
]
```
* **findCards**
Returns an array of card IDs for a given query (functionally identical to **guiBrowse** but doesn't use the GUI
for better performance).
*Sample request*:
```
{
"action": "findCards",
2017-07-02 00:12:11 +00:00
"params": {
"query": "deck:current"
2017-05-28 20:22:21 +00:00
}
}
```
*Sample response*:
```
[
1494723142483,
1494703460437,
2017-07-02 00:12:11 +00:00
1494703479525
2017-05-28 20:22:21 +00:00
]
```
* **guiBrowse**
Invokes the card browser and searches for a given query. Returns an array of identifiers of the cards that were found.
*Sample request*:
```
{
"action": "guiBrowse",
"params": {
"query": "deck:current"
}
}
```
*Sample response*:
```
[
1494723142483,
1494703460437,
1494703479525
]
```
2017-05-28 20:22:21 +00:00
* **guiAddCards**
Invokes the AddCards dialog.
*Sample request*:
```
{
2017-07-02 00:29:48 +00:00
"action": "guiAddCards"
2017-05-28 20:22:21 +00:00
}
```
*Sample response*:
```
2017-05-28 20:55:24 +00:00
null
2017-05-28 20:22:21 +00:00
```
2017-07-01 20:16:51 +00:00
* **guiCurrentCard**
2017-07-01 20:16:51 +00:00
Returns information about the current card or `null` if not in review mode.
*Sample request*:
```
{
2017-07-02 00:29:48 +00:00
"action": "guiCurrentCard"
}
```
*Sample response*:
```
{
2017-07-02 00:12:11 +00:00
"answer": "back content",
"question": "front content",
"deckName": "Default",
"modelName": "Basic",
"fieldOrder": 0,
2017-07-03 00:52:57 +00:00
"fields": {
"Front": {
"value": "front content",
"order": 0
},
"Back": {
"value": "back content",
"order": 1
}
2017-07-03 00:52:57 +00:00
},
2017-07-02 00:12:11 +00:00
"cardId": 1498938915662,
"buttons": [1, 2, 3]
}
```
* **guiShowQuestion**
2017-07-01 20:16:51 +00:00
Shows question text for the current card; returns `true` if in review mode or `false` otherwise.
*Sample request*:
```
{
2017-07-02 00:29:48 +00:00
"action": "guiShowQuestion"
}
```
*Sample response*:
```
true
```
* **guiShowAnswer**
2017-07-01 20:16:51 +00:00
Shows answer text for the current card; returns `true` if in review mode or `false` otherwise.
*Sample request*:
```
{
2017-07-02 00:29:48 +00:00
"action": "guiShowAnswer"
}
```
*Sample response*:
```
true
```
* **guiAnswerCard**
2017-07-01 20:16:51 +00:00
Answers the current card; returns `true` if succeeded or `false` otherwise. Note that the answer for the current
card must be displayed before before any answer can be accepted by Anki.
*Sample request*:
```
{
2017-07-02 00:12:11 +00:00
"action": "guiAnswerCard",
"params": {
"ease": 1
}
}
```
2017-07-02 17:50:30 +00:00
*Sample response*:
```
true
```
2017-07-03 00:33:59 +00:00
2017-07-02 17:50:30 +00:00
* **guiDeckOverview**
Opens the Deck Overview screen for the deck with the given name; returns `true` if succeeded or `false` otherwise.
2017-07-02 17:50:30 +00:00
*Sample request*:
```
{
"action": "guiDeckOverview",
"params": {
"name": "Default"
}
}
```
*Sample response*:
```
true
```
* **guiDeckBrowser**
Opens the Deck Browser screen.
2017-07-02 17:50:30 +00:00
*Sample request*:
```
{
"action": "guiDeckBrowser"
}
```
*Sample response*:
```
null
```
2017-07-03 00:33:59 +00:00
* **guiDeckReview**
Starts review for the deck with the given name; returns `true` if succeeded or `false` otherwise.
*Sample request*:
```
{
"action": "guiDeckReview",
"params": {
"name": "Default"
}
}
```
*Sample response*:
```
true
2017-07-03 00:33:59 +00:00
```
2017-04-22 06:36:47 +00:00
* **upgrade**
2017-02-19 23:05:17 +00:00
Displays a confirmation dialog box in Anki asking the user if they wish to upgrade AnkiConnect to the latest version
from the project's [master branch](https://raw.githubusercontent.com/FooSoft/anki-connect/master/AnkiConnect.py) on
GitHub. Returns a boolean value indicating if the plugin was upgraded or not.
2017-04-22 06:36:47 +00:00
*Sample request*:
2017-02-19 23:05:17 +00:00
```
{
2017-07-02 00:29:48 +00:00
"action": "upgrade"
2017-02-19 23:05:17 +00:00
}
```
2017-04-22 06:36:47 +00:00
*Sample response*:
2017-02-19 23:05:17 +00:00
```
true
```
2016-07-03 02:46:01 +00:00
## License ##
2017-07-31 00:32:28 +00:00
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.