diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6b5d478..c2b0f5c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,24 @@ jobs: - name: Anki 2.1.53 (Qt6) python: 3.9 environment: py39-anki2.1.53-qt6 + - name: Anki 2.1.54 (Qt5) + python: 3.9 + environment: py39-anki2.1.54-qt5 + - name: Anki 2.1.54 (Qt6) + python: 3.9 + environment: py39-anki2.1.54-qt6 + - name: Anki 2.1.55 (Qt5) + python: 3.9 + environment: py39-anki2.1.55-qt5 + - name: Anki 2.1.55 (Qt6) + python: 3.9 + environment: py39-anki2.1.55-qt6 + - name: Anki 2.1.56 (Qt5) + python: 3.9 + environment: py39-anki2.1.56-qt5 + - name: Anki 2.1.56 (Qt6) + python: 3.9 + environment: py39-anki2.1.56-qt6 fail-fast: false steps: diff --git a/README.md b/README.md index 8a90f2e..585e9ff 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,10 @@ plugin will start an HTTP server on port 8765 whenever Anki is launched. Other a extensions) can then communicate with it via HTTP requests. By default, Anki-Connect will only bind the HTTP server to the `127.0.0.1` IP address, so that you will only be able to -access it from the same host on which it is running. If you need to access it over a network, you can set the -environment variable `ANKICONNECT_BIND_ADDRESS` to change the binding address. For example, you can set it to `0.0.0.0` -in order to bind it to all network interfaces on your host. +access it from the same host on which it is running. If you need to access it over a network, you can change the +binding address in the configuration. Go to Tools->Add-ons->AnkiConnect->Config and change the "webBindAddress" +value. For example, you can set it to `0.0.0.0` in order to bind it to all network interfaces on your host. This also +requires a restart for Anki. ### Sample Invocation @@ -171,7 +172,7 @@ be serviced*. Make sure that your pull request meets the following criteria: * Have accompanying tests that verify operation. * Implement features useful in other applications. -### Supported Actions +## Supported Actions Documentation for currently supported actions is split up by category and is referenced below. Note that deprecated APIs will continue to function despite not being listed on this page as long as your request is labeled with a version number @@ -186,13 +187,17 @@ corresponding to when the API was available for use. * [Note Actions](#note-actions) * [Statistic Actions](#statistic-actions) -#### Card Actions +--- -* **getEaseFactors** +### Card Actions - Returns an array with the ease factor for each of the given cards (in the same order). +#### `getEaseFactors` + +* Returns an array with the ease factor for each of the given cards (in the same order). + +
+ Sample request: - *Sample request*: ```json { "action": "getEaseFactors", @@ -202,20 +207,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [4100, 3900], "error": null } ``` +
-* **setEaseFactors** +#### `setEaseFactors` - Sets ease factor of cards by card ID; returns `true` if successful (all cards existed) or `false` otherwise. +* Sets ease factor of cards by card ID; returns `true` if successful (all cards existed) or `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "setEaseFactors", @@ -226,23 +237,29 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [true, true], "error": null } ``` +
-* **setSpecificValueOfCard** +#### `setSpecificValueOfCard` - Sets specific value of a single card. Given the risk of wreaking havor in the database when changing some of the values of a card, some of the keys require the argument "warning_check" set to True. +* Sets specific value of a single card. Given the risk of wreaking havor in the database when changing some of the values of a card, some of the keys require the argument "warning_check" set to True. This can be used to set a card's flag, change it's ease factor, change the review order in a filtered deck and change the column "data" (not currently used by anki apparantly), and many other values. A list of values and explanation of their respective utility can be found at [AnkiDroid's wiki](https://github.com/ankidroid/Anki-Android/wiki/Database-Structure). - *Sample request*: +
+ Sample request: + ```json { "action": "setSpecificValueOfCard", @@ -254,22 +271,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [true, true], "error": null } ``` +
-* **suspend** +#### `suspend` - Suspend cards by card ID; returns `true` if successful (at least one card wasn't already suspended) or `false` +* Suspend cards by card ID; returns `true` if successful (at least one card wasn't already suspended) or `false` otherwise. - *Sample request*: +
+ Sample request: + ```json { "action": "suspend", @@ -279,21 +302,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **unsuspend** +#### `unsuspend` - Unsuspend cards by card ID; returns `true` if successful (at least one card was previously suspended) or `false` +* Unsuspend cards by card ID; returns `true` if successful (at least one card was previously suspended) or `false` otherwise. - *Sample request*: +
+ Sample request: + ```json { "action": "unsuspend", @@ -303,20 +332,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **suspended** +#### `suspended` - Check if card is suspended by its ID. Returns `true` if suspended, `false` otherwise. +* Check if card is suspended by its ID. Returns `true` if suspended, `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "suspended", @@ -326,21 +361,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **areSuspended** +#### `areSuspended` - Returns an array indicating whether each of the given cards is suspended (in the same order). If card doesn't +* Returns an array indicating whether each of the given cards is suspended (in the same order). If card doesn't exist returns `null`. - *Sample request*: +
+ Sample request: + ```json { "action": "areSuspended", @@ -350,22 +391,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [false, true, null], "error": null } ``` +
-* **areDue** +#### `areDue` - Returns an array indicating whether each of the given cards is due (in the same order). *Note*: cards in the +* Returns an array indicating whether each of the given cards is due (in the same order). *Note*: cards in the learning queue with a large interval (over 20 minutes) are treated as not due until the time of their interval has passed, to match the way Anki treats them when reviewing. - *Sample request*: +
+ Sample request: + ```json { "action": "areDue", @@ -375,21 +422,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [false, true], "error": null } ``` +
-* **getIntervals** +#### `getIntervals` - Returns an array of the most recent intervals for each given card ID, or a 2-dimensional array of all the intervals +* Returns an array of the most recent intervals for each given card ID, or a 2-dimensional array of all the intervals for each given card ID when `complete` is `true`. Negative intervals are in seconds and positive intervals in days. - *Sample request 1*: +
+ Sample request 1: + ```json { "action": "getIntervals", @@ -399,16 +452,22 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result 1: - *Sample result 1*: ```json { "result": [-14400, 3], "error": null } ``` +
+ +
+ Sample request 2: - *Sample request 2*: ```json { "action": "getIntervals", @@ -419,8 +478,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result 2: - *Sample result 2*: ```json { "result": [ @@ -430,13 +492,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **findCards** +#### `findCards` - Returns an array of card IDs for a given query. Functionally identical to `guiBrowse` but doesn't use the GUI for +* 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*: +
+ Sample request: + ```json { "action": "findCards", @@ -446,21 +511,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1494723142483, 1494703460437, 1494703479525], "error": null } ``` +
-* **cardsToNotes** +#### `cardsToNotes` - Returns an unordered array of note IDs for the given card IDs. For cards with the same note, the ID is only given +* Returns an unordered array of note IDs for the given card IDs. For cards with the same note, the ID is only given once in the array. - *Sample request*: +
+ Sample request: + ```json { "action": "cardsToNotes", @@ -470,21 +541,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1502098029797, 1502298025183], "error": null } ``` +
-* **cardsModTime** +#### `cardsModTime` - Returns a list of objects containings for each card ID the modification time. +* Returns a list of objects containings for each card ID the modification time. This function is about 15 times faster than executing `cardsInfo`. - *Sample request*: +
+ Sample request: + ```json { "action": "cardsModTime", @@ -494,8 +571,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -507,14 +587,17 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **cardsInfo** +#### `cardsInfo` - Returns a list of objects containing for each card ID the card fields, front and back sides including CSS, note +* Returns a list of objects containing for each card ID the card fields, front and back sides including CSS, note type, the note that the card belongs to, and deck name, last modification timestamp as well as ease and interval. - *Sample request*: +
+ Sample request: + ```json { "action": "cardsInfo", @@ -524,8 +607,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -578,12 +664,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **forgetCards** +#### `forgetCards` - Forget cards, making the cards new again. +* Forget cards, making the cards new again. + +
+ Sample request: - *Sample request*: ```json { "action": "forgetCards", @@ -593,20 +682,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **relearnCards** +#### `relearnCards` - Make cards be "relearning". +* Make cards be "relearning". + +
+ Sample request: - *Sample request*: ```json { "action": "relearnCards", @@ -616,63 +711,83 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-#### Deck Actions +--- -* **deckNames** +### Deck Actions - Gets the complete list of deck names for the current user. +#### `deckNames` + +* Gets the complete list of deck names for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "deckNames", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["Default"], "error": null } ``` +
-* **deckNamesAndIds** +#### `deckNamesAndIds` - Gets the complete list of deck names and their respective IDs for the current user. +* Gets the complete list of deck names and their respective IDs for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "deckNamesAndIds", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": {"Default": 1}, "error": null } ``` +
-* **getDecks** +#### `getDecks` - Accepts an array of card IDs and returns an object with each deck name as a key, and its value an array of the given +* Accepts an array of card IDs and returns an object with each deck name as a key, and its value an array of the given cards which belong to it. - *Sample request*: +
+ Sample request: + ```json { "action": "getDecks", @@ -682,8 +797,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -693,12 +811,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **createDeck** +#### `createDeck` - Create a new empty deck. Will not overwrite a deck that exists with the same name. +* Create a new empty deck. Will not overwrite a deck that exists with the same name. + +
+ Sample request: - *Sample request*: ```json { "action": "createDeck", @@ -708,19 +829,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1519323742721, "error": null } ``` -* **changeDeck** +
- Moves cards with the given IDs to a different deck, creating the deck if it doesn't exist yet. +#### `changeDeck` + +* Moves cards with the given IDs to a different deck, creating the deck if it doesn't exist yet. + +
+ Sample request: - *Sample request*: ```json { "action": "changeDeck", @@ -731,21 +859,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **deleteDecks** +#### `deleteDecks` - Deletes decks with the given names. +* Deletes decks with the given names. The argument `cardsToo` *must* be specified and set to `true`. - *Sample request*: +
+ Sample request: + ```json { "action": "deleteDecks", @@ -756,20 +890,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **getDeckConfig** +#### `getDeckConfig` - Gets the configuration group object for the given deck. +* Gets the configuration group object for the given deck. + +
+ Sample request: - *Sample request*: ```json { "action": "getDeckConfig", @@ -779,8 +919,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -822,13 +965,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **saveDeckConfig** +#### `saveDeckConfig` - Saves the given configuration group, returning `true` on success or `false` if the ID of the configuration group is +* Saves the given configuration group, returning `true` on success or `false` if the ID of the configuration group is invalid (such as when it does not exist). - *Sample request*: +
+ Sample request: + ```json { "action": "saveDeckConfig", @@ -873,21 +1019,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **setDeckConfigId** +#### `setDeckConfigId` - Changes the configuration group for the given decks to the one with the given ID. Returns `true` on success or +* 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*: +
+ Sample request: + ```json { "action": "setDeckConfigId", @@ -898,22 +1050,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **cloneDeckConfigId** +#### `cloneDeckConfigId` - Creates a new configuration group with the given name, cloning from the group with the given ID, or from the default +* Creates a new configuration 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 configuration group, or `false` if the specified group to clone from does not exist. - *Sample request*: +
+ Sample request: + ```json { "action": "cloneDeckConfigId", @@ -924,21 +1082,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1502972374573, "error": null } ``` +
-* **removeDeckConfigId** +#### `removeDeckConfigId` - Removes the configuration group with the given ID, returning `true` if successful, or `false` if attempting to +* Removes the configuration group with the given ID, returning `true` if successful, or `false` if attempting to remove either the default configuration group (ID = 1) or a configuration group that does not exist. - *Sample request*: +
+ Sample request: + ```json { "action": "removeDeckConfigId", @@ -948,20 +1112,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **getDeckStats** +#### `getDeckStats` - Gets statistics such as total cards and cards due for the given decks. +* Gets statistics such as total cards and cards due for the given decks. + +
+ Sample request: - *Sample request*: ```json { "action": "getDeckStats", @@ -971,8 +1141,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -996,15 +1169,20 @@ corresponding to when the API was available for use. "error": null } ``` +
-#### Graphical Actions +--- -* **guiBrowse** +### Graphical Actions - Invokes the *Card Browser* dialog and searches for a given query. Returns an array of identifiers of the cards that +#### `guiBrowse` + +* Invokes the *Card Browser* dialog and searches for a given query. Returns an array of identifiers of the cards that were found. Query syntax is [documented here](https://docs.ankiweb.net/searching.html). - *Sample request*: +
+ Sample request: + ```json { "action": "guiBrowse", @@ -1014,39 +1192,49 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1494723142483, 1494703460437, 1494703479525], "error": null } ``` +
-* **guiSelectedNotes** +#### `guiSelectedNotes` - Finds the open instance of the *Card Browser* dialog and returns an array of identifiers of the notes that are +* Finds the open instance of the *Card Browser* dialog and returns an array of identifiers of the notes that are selected. Returns an empty list if the browser is not open. - *Sample request*: +
+ Sample request: + ```json { "action": "guiSelectedNotes", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1494723142483, 1494703460437, 1494703479525], "error": null } ``` +
-* **guiAddCards** +#### `guiAddCards` - Invokes the *Add Cards* dialog, presets the note using the given deck and model, with the provided field values and tags. +* Invokes the *Add Cards* dialog, presets the note using the given deck and model, with the provided field values and tags. Invoking it multiple times closes the old window and _reopen the window_ with the new provided values. Audio, video, and picture files can be embedded into the fields via the `audio`, `video`, and `picture` keys, respectively. @@ -1054,7 +1242,9 @@ corresponding to when the API was available for use. The result is the ID of the note which would be added, if the user chose to confirm the *Add Cards* dialogue. - *Sample request*: +
+ Sample request: + ```json { "action": "guiAddCards", @@ -1081,25 +1271,32 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1496198395707, "error": null } ``` +
-* **guiEditNote** +#### `guiEditNote` - Opens the *Edit* dialog with a note corresponding to given note ID. +* Opens the *Edit* dialog with a note corresponding to given note ID. The dialog is similar to the *Edit Current* dialog, but: + * has a Preview button to preview the cards for the note * has a Browse button to open the browser with these cards * has Previous/Back buttons to navigate the history of the dialog * has no bar with the Close button - *Sample request*: +
+ Sample request: + ```json { "action": "guiEditNote", @@ -1109,28 +1306,37 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **guiCurrentCard** +#### `guiCurrentCard` - Returns information about the current card or `null` if not in review mode. +* Returns information about the current card or `null` if not in review mode. + +
+ Sample request: - *Sample request*: ```json { "action": "guiCurrentCard", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -1151,75 +1357,96 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **guiStartCardTimer** +#### `guiStartCardTimer` - Starts or resets the `timerStarted` value for the current card. This is useful for deferring the start time to when +* Starts or resets the `timerStarted` value for the current card. This is useful for deferring the start time to when it is displayed via the API, allowing the recorded time taken to answer the card to be more accurate when calling `guiAnswerCard`. - *Sample request*: +
+ Sample request: + ```json { "action": "guiStartCardTimer", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiShowQuestion** +#### `guiShowQuestion` - Shows question text for the current card; returns `true` if in review mode or `false` otherwise. +* Shows question text for the current card; returns `true` if in review mode or `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "guiShowQuestion", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiShowAnswer** +#### `guiShowAnswer` - Shows answer text for the current card; returns `true` if in review mode or `false` otherwise. +* Shows answer text for the current card; returns `true` if in review mode or `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "guiShowAnswer", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiAnswerCard** +#### `guiAnswerCard` - Answers the current card; returns `true` if succeeded or `false` otherwise. Note that the answer for the current +* 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*: +
+ Sample request: + ```json { "action": "guiAnswerCard", @@ -1229,20 +1456,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiDeckOverview** +#### `guiDeckOverview` - Opens the *Deck Overview* dialog for the deck with the given name; returns `true` if succeeded or `false` otherwise. +* Opens the *Deck Overview* dialog for the deck with the given name; returns `true` if succeeded or `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "guiDeckOverview", @@ -1252,40 +1485,52 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiDeckBrowser** +#### `guiDeckBrowser` - Opens the *Deck Browser* dialog. +* Opens the *Deck Browser* dialog. + +
+ Sample request: - *Sample request*: ```json { "action": "guiDeckBrowser", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **guiDeckReview** +#### `guiDeckReview` - Starts review for the deck with the given name; returns `true` if succeeded or `false` otherwise. +* Starts review for the deck with the given name; returns `true` if succeeded or `false` otherwise. + +
+ Sample request: - *Sample request*: ```json { "action": "guiDeckReview", @@ -1295,66 +1540,86 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **guiExitAnki** +#### `guiExitAnki` - Schedules a request to gracefully close Anki. This operation is asynchronous, so it will return immediately and +* Schedules a request to gracefully close Anki. This operation is asynchronous, so it will return immediately and won't wait until the Anki process actually terminates. - *Sample request*: +
+ Sample request: + ```json { "action": "guiExitAnki", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **guiCheckDatabase** +#### `guiCheckDatabase` - Requests a database check, but returns immediately without waiting for the check to complete. Therefore, the action will always return `true` even if errors are detected during the database check. +* Requests a database check, but returns immediately without waiting for the check to complete. Therefore, the action will always return `true` even if errors are detected during the database check. + +
+ Sample request: - *Sample request*: ```json { "action": "guiCheckDatabase", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-#### Media Actions +--- -* **storeMediaFile** +### Media Actions - Stores a file with the specified base64-encoded contents inside the media folder. Alternatively you can specify a +#### `storeMediaFile` + +* Stores a file with the specified base64-encoded contents inside the media folder. Alternatively you can specify a absolute file path, or a url from where the file shell be downloaded. If more than one of `data`, `path` and `url` are provided, the `data` field will be used first, then `path`, and finally `url`. To prevent Anki from removing files not used by any cards (e.g. for configuration files), prefix the filename with an underscore. These files are still synchronized to AnkiWeb. Any existing file with the same name is deleted by default. Set `deleteExisting` to false to prevent that by [letting Anki give the new file a non-conflicting name](https://github.com/ankitects/anki/blob/aeba725d3ea9628c73300648f748140db3fdd5ed/rslib/src/media/files.rs#L194). - *Sample request*: +
+ Sample request (relative path): + ```json { "action": "storeMediaFile", @@ -1366,20 +1631,27 @@ corresponding to when the API was available for use. } ``` - *Sample result*: + *Content of `_hello.txt`*: + + ``` + Hello world! + ``` +
+ +
+ Sample result (relative path): + ```json { "result": "_hello.txt", "error": null } ``` +
- *Content of `_hello.txt`*: - ``` - Hello world! - ``` +
+ Sample request (absolute path): - *Sample request*: ```json { "action": "storeMediaFile", @@ -1390,16 +1662,22 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result (absolute path): - *Sample result*: ```json { "result": "_hello.txt", "error": null } ``` +
+ +
+ Sample request (url): - *Sample request*: ```json { "action": "storeMediaFile", @@ -1410,20 +1688,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result (url): - *Sample result*: ```json { "result": "_hello.txt", "error": null } ``` +
-* **retrieveMediaFile** +#### `retrieveMediaFile` - Retrieves the base64-encoded contents of the specified file, returning `false` if the file does not exist. +* Retrieves the base64-encoded contents of the specified file, returning `false` if the file does not exist. + +
+ Sample request: - *Sample request*: ```json { "action": "retrieveMediaFile", @@ -1433,20 +1717,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": "SGVsbG8sIHdvcmxkIQ==", "error": null } ``` +
-* **getMediaFilesNames** +#### `getMediaFilesNames` - Gets the names of media files matched the pattern. Returning all names by default. +* Gets the names of media files matched the pattern. Returning all names by default. + +
+ Sample request: - *Sample request*: ```json { "action": "getMediaFilesNames", @@ -1456,20 +1746,52 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["_hello.txt"], "error": null } ``` +
-* **deleteMediaFile** +#### `getMediaDirPath` - Deletes the specified file inside the media folder. +* Gets the full path to the `collection.media` folder of the currently opened profile. + +
+ Sample request: + + ```json + { + "action": "getMediaDirPath", + "version": 6 + } + ``` +
+ +
+ Sample result: + + ```json + { + "result": "/home/user/.local/share/Anki2/Main/collection.media", + "error": null + } + ``` +
+ +#### `deleteMediaFile` + +* Deletes the specified file inside the media folder. + +
+ Sample request: - *Sample request*: ```json { "action": "deleteMediaFile", @@ -1479,20 +1801,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-#### Miscellaneous Actions +--- -* **requestPermission** +### Miscellaneous Actions - Requests permission to use the API exposed by this plugin. This method does not require the API key, and is the +#### `requestPermission` + +* Requests permission to use the API exposed by this plugin. This method does not require the API key, and is the only one that accepts requests from any origin; the other methods only accept requests from trusted origins, which are listed under `webCorsOriginList` in the add-on config. `localhost` is trusted by default. @@ -1509,15 +1837,20 @@ corresponding to when the API was available for use. properly with each other. New versions of Anki-Connect are backwards compatible; as long as you are using actions which are available in the reported Anki-Connect version or earlier, everything should work fine. - *Sample request*: +
+ Sample request: + ```json { "action": "requestPermission", "version": 6 } ``` +
+ +
+ Samples results: - *Samples results*: ```json { "result": { @@ -1528,6 +1861,7 @@ corresponding to when the API was available for use. "error": null } ``` + ```json { "result": { @@ -1536,31 +1870,38 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **version** +#### `version` - Gets the version of the API exposed by this plugin. Currently versions `1` through `6` are defined. +* Gets the version of the API exposed by this plugin. Currently versions `1` through `6` are defined. + +
+ Sample request: - *Sample request*: ```json { "action": "version", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 6, "error": null } ``` +
-* **apiReflect** +#### `apiReflect` - Gets information about the AnkiConnect APIs available. The request supports the following params: +* Gets information about the AnkiConnect APIs available. The request supports the following params: * `scopes` - An array of scopes to get reflection information about. The only currently supported value is `"actions"`. @@ -1571,7 +1912,9 @@ corresponding to when the API was available for use. The result will contain a list of which scopes were used and a value for each scope. For example, the `"actions"` scope will contain a `"actions"` property which contains a list of supported action names. - *Sample request*: +
+ Sample request: + ```json { "action": "apiReflect", @@ -1582,8 +1925,11 @@ corresponding to when the API was available for use. "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -1593,52 +1939,67 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **sync** +#### `sync` - Synchronizes the local Anki collections with AnkiWeb. +* Synchronizes the local Anki collections with AnkiWeb. + +
+ Sample request: - *Sample request*: ```json { "action": "sync", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **getProfiles** +#### `getProfiles` - Retrieve the list of profiles. +* Retrieve the list of profiles. + +
+ Sample request: - *Sample request*: ```json { "action": "getProfiles", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["User 1"], "error": null } ``` +
-* **loadProfile** +#### `loadProfile` - Selects the profile specified in request. +* Selects the profile specified in request. + +
+ Sample request: - *Sample request*: ```json { "action": "loadProfile", @@ -1648,20 +2009,26 @@ corresponding to when the API was available for use. "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **multi** +#### `multi` - Performs multiple actions in one request, returning an array with the response of each action (in the given order). +* Performs multiple actions in one request, returning an array with the response of each action (in the given order). + +
+ Sample request: - *Sample request*: ```json { "action": "multi", @@ -1688,8 +2055,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -1701,13 +2071,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **exportPackage** +#### `exportPackage` - Exports a given deck in `.apkg` format. Returns `true` if successful or `false` otherwise. The optional property +* Exports a given deck in `.apkg` format. Returns `true` if successful or `false` otherwise. The optional property `includeSched` (default is `false`) can be specified to include the cards' scheduling data. - *Sample request*: +
+ Sample request: + ```json { "action": "exportPackage", @@ -1719,21 +2092,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **importPackage** +#### `importPackage` - Imports a file in `.apkg` format into the collection. Returns `true` if successful or `false` otherwise. +* Imports a file in `.apkg` format into the collection. Returns `true` if successful or `false` otherwise. Note that the file path is relative to Anki's collection.media folder, not to the client. - *Sample request*: +
+ Sample request: + ```json { "action": "importPackage", @@ -1743,70 +2122,93 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-* **reloadCollection** +#### `reloadCollection` - Tells anki to reload all data from the database. +* Tells anki to reload all data from the database. + +
+ Sample request: - *Sample request*: ```json { "action": "reloadCollection", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-#### Model Actions +--- -* **modelNames** +### Model Actions - Gets the complete list of model names for the current user. +#### `modelNames` + +* Gets the complete list of model names for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "modelNames", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["Basic", "Basic (and reversed card)"], "error": null } ``` +
-* **modelNamesAndIds** +#### `modelNamesAndIds` - Gets the complete list of model names and their corresponding IDs for the current user. +* Gets the complete list of model names and their corresponding IDs for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "modelNamesAndIds", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -1818,12 +2220,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **modelFieldNames** +#### `modelFieldNames` - Gets the complete list of field names for the provided model name. +* Gets the complete list of field names for the provided model name. + +
+ Sample request: - *Sample request*: ```json { "action": "modelFieldNames", @@ -1833,20 +2238,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["Front", "Back"], "error": null } ``` +
-* **modelFieldDescriptions** +#### `modelFieldDescriptions` - Gets the complete list of field descriptions (the text seen in the gui editor when a field is empty) for the provided model name. +* Gets the complete list of field descriptions (the text seen in the gui editor when a field is empty) for the provided model name. + +
+ Sample request: - *Sample request*: ```json { "action": "modelFieldDescriptions", @@ -1856,20 +2267,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["", ""], "error": null } ``` +
-* **modelFieldFonts** +#### `modelFieldFonts` - Gets the complete list of fonts along with their font sizes. +* Gets the complete list of fonts along with their font sizes. + +
+ Sample request: - *Sample request*: ```json { "action": "modelFieldFonts", @@ -1879,8 +2296,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -1896,13 +2316,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **modelFieldsOnTemplates** +#### `modelFieldsOnTemplates` - Returns an object indicating the fields on the question and answer side of each card template for the given model +* Returns an object indicating the fields on the question and answer side of each card template for the given model name. The question side is given first in each array. - *Sample request*: +
+ Sample request: + ```json { "action": "modelFieldsOnTemplates", @@ -1912,8 +2335,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -1923,16 +2349,19 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **createModel** +#### `createModel` - Creates a new model to be used in Anki. User must provide the `modelName`, `inOrderFields` and `cardTemplates` to be +* Creates a new model to be used in Anki. User must provide the `modelName`, `inOrderFields` and `cardTemplates` to be used in the model. There are optional fields `css` and `isCloze`. If not specified, `css` will use the default Anki css and `isCloze` will be equal to `False`. If `isCloze` is `True` then model will be created as Cloze. Optionally the `Name` field can be provided for each entry of `cardTemplates`. By default the card names will be `Card 1`, `Card 2`, and so on. - *Sample request* +
+ Sample request + ```json { "action": "createModel", @@ -1952,8 +2381,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result - *Sample result* ```json { "result":{ @@ -2007,7 +2439,7 @@ corresponding to when the API was available for use. "tags":[ ], - "id":"1551462107104", + "id":1551462107104, "req":[ [ 0, @@ -2021,12 +2453,15 @@ corresponding to when the API was available for use. "error":null } ``` +
-* **modelTemplates** +#### `modelTemplates` - Returns an object indicating the template content for each card connected to the provided model by name. +* Returns an object indicating the template content for each card connected to the provided model by name. + +
+ Sample request: - *Sample request*: ```json { "action": "modelTemplates", @@ -2036,8 +2471,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result - *Sample result* ```json { "result": { @@ -2053,12 +2491,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **modelStyling** +#### `modelStyling` - Gets the CSS styling for the provided model by name. +* Gets the CSS styling for the provided model by name. + +
+ Sample request: - *Sample request*: ```json { "action": "modelStyling", @@ -2068,8 +2509,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result - *Sample result* ```json { "result": { @@ -2078,13 +2522,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **updateModelTemplates** +#### `updateModelTemplates` - Modify the templates of an existing model by name. Only specifies cards and specified sides will be modified. +* Modify the templates of an existing model by name. Only specifies cards and specified sides will be modified. If an existing card or side is not included in the request, it will be left unchanged. - *Sample request*: +
+ Sample request: + ```json { "action": "updateModelTemplates", @@ -2102,20 +2549,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **updateModelStyling** +#### `updateModelStyling` - Modify the CSS styling of an existing model by name. +* Modify the CSS styling of an existing model by name. + +
+ Sample request: - *Sample request*: ```json { "action": "updateModelStyling", @@ -2128,20 +2581,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **findAndReplaceInModels** +#### `findAndReplaceInModels` - Find and replace string in existing model by model name. Customise to replace in front, back or css by setting to true/false. +* Find and replace string in existing model by model name. Customise to replace in front, back or css by setting to true/false. + +
+ Sample request: - *Sample request*: ```json { "action": "findAndReplaceInModels", @@ -2158,20 +2617,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1, "error": null } ``` +
-* **modelFieldRename** +#### `modelFieldRename` - Rename the field name of a given model. +* Rename the field name of a given model. + +
+ Sample request: - *Sample Request*: ```json { "action": "modelFieldRename", @@ -2183,22 +2648,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldReposition** +#### `modelFieldReposition` - Reposition the field within the field list of a given model. +* Reposition the field within the field list of a given model. The value of `index` starts at 0. For example, an index of `0` puts the field in the first position, and an index of `2` puts the field in the third position. - *Sample Request*: +
+ Sample request: + ```json { "action": "modelFieldReposition", @@ -2210,22 +2681,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldAdd** +#### `modelFieldAdd` - Creates a new field within a given model. +* Creates a new field within a given model. Optionally, the `index` value can be provided, which works exactly the same as the index in `modelFieldReposition`. By default, the field is added to the end of the field list. - *Sample Request*: +
+ Sample request: + ```json { "action": "modelFieldAdd", @@ -2237,20 +2714,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldRemove** +#### `modelFieldRemove` - Deletes a field within a given model. +* Deletes a field within a given model. + +
+ Sample request: - *Sample Request*: ```json { "action": "modelFieldRemove", @@ -2261,20 +2744,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldSetFont** +#### `modelFieldSetFont` - Sets the font for a field within a given model. +* Sets the font for a field within a given model. + +
+ Sample request: - *Sample Request*: ```json { "action": "modelFieldSetFont", @@ -2286,20 +2775,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldSetFontSize** +#### `modelFieldSetFontSize` - Sets the font size for a field within a given model. +* Sets the font size for a field within a given model. + +
+ Sample request: - *Sample Request*: ```json { "action": "modelFieldSetFont", @@ -2311,22 +2806,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **modelFieldSetDescription** +#### `modelFieldSetDescription` - Sets the description (the text seen in the gui editor when a field is empty) for a field within a given model. +* Sets the description (the text seen in the gui editor when a field is empty) for a field within a given model. Older versions of Anki (2.1.49 and below) do not have field descriptions. In that case, this will return with `false`. - *Sample Request*: +
+ Sample request: + ```json { "action": "modelFieldSetDescription", @@ -2338,20 +2839,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": true, "error": null } ``` +
-#### Note Actions +--- -* **addNote** +### Note Actions - Creates a note using the given deck and model, with the provided field values and tags. Returns the identifier of +#### `addNote` + +* 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. Anki-Connect can download audio, video, and picture files and embed them in newly created notes. The corresponding `audio`, `video`, and `picture` note members are @@ -2372,7 +2879,9 @@ corresponding to when the API was available for use. * `duplicateScopeOptions.checkChildren` will change whether or not duplicate cards are checked in child decks. The default value is `false`. * `duplicateScopeOptions.checkAllModels` specifies whether duplicate checks are performed across all note types. The default value is `false`. - *Sample request*: +
+ Sample request: + ```json { "action": "addNote", @@ -2425,22 +2934,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1496198395707, "error": null } ``` +
-* **addNotes** +#### `addNotes` - Creates multiple notes using the given deck and model, with the provided field values and tags. Returns an array of +* 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. - *Sample request*: +
+ Sample request: + ```json { "action": "addNotes", @@ -2486,21 +3001,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1496198395707, null], "error": null } ``` +
-* **canAddNotes** +#### `canAddNotes` - Accepts an array of objects which define parameters for candidate notes (see `addNote`) and returns an array of +* 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. - *Sample request*: +
+ Sample request: + ```json { "action": "canAddNotes", @@ -2522,26 +3043,32 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [true], "error": null } ``` +
-* **updateNoteFields** +#### `updateNoteFields` - Modify the fields of an existing note. You can also include audio, video, or picture files which will be added to the note with an +* Modify the fields of an existing note. You can also include audio, video, or picture files which will be added to the note with an optional `audio`, `video`, or `picture` property. Please see the documentation for `addNote` for an explanation of objects in the `audio`, `video`, or `picture` array. - > **Warning** + > **Warning**: > You must not be viewing the note that you are updating on your Anki browser, otherwise > the fields will not update. See [this issue](https://github.com/FooSoft/anki-connect/issues/82) > for further details. - *Sample request*: +
+ Sample request: + ```json { "action": "updateNoteFields", @@ -2565,18 +3092,22 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **updateNote** +#### `updateNote` - Modify the fields and/or tags of an existing note. +* Modify the fields and/or tags of an existing note. In other words, combines `updateNoteFields` and `updateNoteTags`. Please see their documentation for an explanation of all properties. @@ -2593,7 +3124,9 @@ corresponding to when the API was available for use. > the fields will not update. See [this issue](https://github.com/FooSoft/anki-connect/issues/82) > for further details. - *Sample request*: +
+ Sample request: + ```json { "action": "updateNote", @@ -2611,7 +3144,11 @@ corresponding to when the API was available for use. } ``` - *Sample result*: +
+ +
+ Sample result: + ```json { "result": null, @@ -2619,11 +3156,15 @@ corresponding to when the API was available for use. } ``` -* **updateNoteTags** +
- Set a note's tags by note ID. Old tags will be removed. +#### `updateNoteTags` + +* Set a note's tags by note ID. Old tags will be removed. + +
+ Sample request: - *Sample request*: ```json { "action": "updateNoteTags", @@ -2635,7 +3176,11 @@ corresponding to when the API was available for use. } ``` - *Sample result*: +
+ +
+ Sample result: + ```json { "result": null, @@ -2643,22 +3188,30 @@ corresponding to when the API was available for use. } ``` -* **getNoteTags** +
- Get a note's tags by note ID. +#### `getNoteTags` + +* Get a note's tags by note ID. + +
+ Sample request: - *Sample request*: ```json { "action": "getNoteTags", "version": 6, "params": { - "note": 1483959289817, + "note": 1483959289817 } } ``` - *Sample result*: +
+ +
+ Sample result: + ```json { "result": ["european-languages"], @@ -2666,11 +3219,15 @@ corresponding to when the API was available for use. } ``` -* **addTags** +
- Adds tags to notes by note ID. +#### `addTags` + +* Adds tags to notes by note ID. + +
+ Sample request: - *Sample request*: ```json { "action": "addTags", @@ -2681,20 +3238,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **removeTags** +#### `removeTags` - Remove tags from notes by note ID. +* Remove tags from notes by note ID. + +
+ Sample request: - *Sample request*: ```json { "action": "removeTags", @@ -2705,60 +3268,78 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **getTags** +#### `getTags` - Gets the complete list of tags for the current user. +* Gets the complete list of tags for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "getTags", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": ["european-languages", "idioms"], "error": null } ``` +
-* **clearUnusedTags** +#### `clearUnusedTags` - Clears all the unused tags in the notes for the current user. +* Clears all the unused tags in the notes for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "clearUnusedTags", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **replaceTags** +#### `replaceTags` - Replace tags in notes by note ID. +* Replace tags in notes by note ID. + +
+ Sample request: - *Sample request*: ```json { "action": "replaceTags", @@ -2770,20 +3351,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **replaceTagsInAllNotes** +#### `replaceTagsInAllNotes` - Replace tags in all the notes for the current user. +* Replace tags in all the notes for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "replaceTagsInAllNotes", @@ -2794,20 +3381,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **findNotes** +#### `findNotes` - Returns an array of note IDs for a given query. Query syntax is [documented here](https://docs.ankiweb.net/searching.html). +* Returns an array of note IDs for a given query. Query syntax is [documented here](https://docs.ankiweb.net/searching.html). + +
+ Sample request: - *Sample request*: ```json { "action": "findNotes", @@ -2817,21 +3410,27 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [1483959289817, 1483959291695], "error": null } ``` +
-* **notesInfo** +#### `notesInfo` - Returns a list of objects containing for each note ID the note fields, tags, note type and the cards belonging to +* Returns a list of objects containing for each note ID the note fields, tags, note type and the cards belonging to the note. - *Sample request*: +
+ Sample request: + ```json { "action": "notesInfo", @@ -2841,8 +3440,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -2859,13 +3461,16 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **deleteNotes** +#### `deleteNotes` - Deletes notes with the given ids. If a note has several cards associated with it, all associated cards will be deleted. +* Deletes notes with the given ids. If a note has several cards associated with it, all associated cards will be deleted. + +
+ Sample request: - *Sample request*: ```json { "action": "deleteNotes", @@ -2875,70 +3480,93 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-* **removeEmptyNotes** +#### `removeEmptyNotes` - Removes all the empty notes for the current user. +* Removes all the empty notes for the current user. + +
+ Sample request: - *Sample request*: ```json { "action": "removeEmptyNotes", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
-#### Statistic Actions +--- -* **getNumCardsReviewedToday** +### Statistic Actions - Gets the count of cards that have been reviewed in the current day (with day start time as configured by user in anki) +#### `getNumCardsReviewedToday` + +* Gets the count of cards that have been reviewed in the current day (with day start time as configured by user in anki) + +
+ Sample request: - *Sample request*: ```json { "action": "getNumCardsReviewedToday", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 0, "error": null } ``` +
-* **getNumCardsReviewedByDay** +#### `getNumCardsReviewedByDay` - Gets the number of cards reviewed as a list of pairs of `(dateString, number)` +* Gets the number of cards reviewed as a list of pairs of `(dateString, number)` + +
+ Sample request: - *Sample request*: ```json { "action": "getNumCardsReviewedByDay", "version": 6 } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -2948,12 +3576,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **getCollectionStatsHTML** +#### `getCollectionStatsHTML` - Gets the collection statistics report +* Gets the collection statistics report + +
+ Sample request: - *Sample request*: ```json { "action": "getCollectionStatsHTML", @@ -2963,22 +3594,28 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "error": null, "result": "
lots of HTML here
" } ``` +
-* **cardReviews** +#### `cardReviews` - Requests all card reviews for a specified deck after a certain time. +* Requests all card reviews for a specified deck after a certain time. `startID` is the latest unix time not included in the result. Returns a list of 9-tuples `(reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)` - *Sample request*: +
+ Sample request: + ```json { "action": "cardReviews", @@ -2989,8 +3626,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": [ @@ -3000,10 +3640,11 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **getReviewsOfCards** +#### `getReviewsOfCards` - Requests all card reviews for each card ID. +* Requests all card reviews for each card ID. Returns a dictionary mapping each card ID to a list of dictionaries of the format: ``` { @@ -3020,7 +3661,9 @@ corresponding to when the API was available for use. The reason why these key values are used instead of the more descriptive counterparts is because these are the exact key values used in Anki's database. - *Sample request*: +
+ Sample request: + ```json { "action": "getReviewsOfCards", @@ -3032,8 +3675,11 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": { @@ -3063,12 +3709,15 @@ corresponding to when the API was available for use. "error": null } ``` +
-* **getLatestReviewID** +#### `getLatestReviewID` - Returns the unix time of the latest review for the given deck. 0 if no review has ever been made for the deck. +* Returns the unix time of the latest review for the given deck. 0 if no review has ever been made for the deck. + +
+ Sample request: - *Sample request*: ```json { "action": "getLatestReviewID", @@ -3078,20 +3727,26 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": 1594194095746, "error": null } ``` +
-* **insertReviews** +#### `insertReviews` - Inserts the given reviews into the database. Required format: list of 9-tuples `(reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)` +* Inserts the given reviews into the database. Required format: list of 9-tuples `(reviewTime, cardID, usn, buttonPressed, newInterval, previousInterval, newFactor, reviewDuration, reviewType)` + +
+ Sample request: - *Sample request*: ```json { "action": "insertReviews", @@ -3104,11 +3759,15 @@ corresponding to when the API was available for use. } } ``` +
+ +
+ Sample result: - *Sample result*: ```json { "result": null, "error": null } ``` +
diff --git a/plugin/__init__.py b/plugin/__init__.py index cfa1dc7..eaefc32 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -350,17 +350,22 @@ class AnkiConnect: # Not a duplicate return 0 + def raiseNotFoundError(self, errorMsg): + if anki_version < (2, 1, 55): + raise NotFoundError(errorMsg) + raise NotFoundError(errorMsg, None, None, None) + def getCard(self, card_id: int) -> Card: try: return self.collection().getCard(card_id) except NotFoundError: - raise NotFoundError('Card was not found: {}'.format(card_id)) + self.raiseNotFoundError('Card was not found: {}'.format(card_id)) def getNote(self, note_id: int) -> Note: try: return self.collection().getNote(note_id) except NotFoundError: - raise NotFoundError('Note was not found: {}'.format(note_id)) + self.raiseNotFoundError('Note was not found: {}'.format(note_id)) def deckStatsToJson(self, due_tree): deckStats = {'deck_id': due_tree.deck_id, @@ -717,6 +722,9 @@ class AnkiConnect: except AttributeError: self.media().trash_files([filename]) + @util.api() + def getMediaDirPath(self): + return os.path.abspath(self.media().dir()) @util.api() def addNote(self, note): diff --git a/tests/test_media.py b/tests/test_media.py index b667c8e..fde46e8 100755 --- a/tests/test_media.py +++ b/tests/test_media.py @@ -1,4 +1,5 @@ import base64 +import os.path from conftest import ac @@ -49,3 +50,7 @@ def test_deleteMediaFile(session_with_profile_loaded): ac.deleteMediaFile(filename=filename_1) assert ac.retrieveMediaFile(filename=filename_1) is False assert ac.getMediaFilesNames(pattern="_tes*.txt") == [filename_2] + + +def test_getMediaDirPath(session_with_profile_loaded): + assert os.path.isdir(ac.getMediaDirPath()) diff --git a/tox.ini b/tox.ini index 699b662..84f4173 100644 --- a/tox.ini +++ b/tox.ini @@ -54,7 +54,7 @@ requires = pypi-timemachine envlist = py38-anki2.1.{45,46,47,48,49} - py39-anki2.1.{50,51,52,53}-qt{5,6} + py39-anki2.1.{50,51,52,53,54,55,56}-qt{5,6} [testenv:.tox] install_command =