= modular python script launcher to patch anki cards en masse
Anki patcher is a script launcher to manipulate anki cards via patch operations defined as python functions.
An operation is a python file with an execute (card_id, fields, config) function. This function gets called for each card of a specified deck and can execute arbitrary code on the card. The card_id and fields are provided by anki pacher and the config is a parsed .yml file provided by the user.
- Option A (local dev):
- run
poetry install - run
poetry shellandpython -m unidic downloadto download the unidic dictionary - add an .env file to the root of the project (based on .env.example)
- run
- Option B (Docker, no local Python required):
- ensure the AnkiConnect add-on is installed and your Anki Desktop app is running (defaults to http://localhost:8765)
- Docker will reach your host's AnkiConnect via
http://host.docker.internal:8765(pre-configured)
To execute an operation locally, run poetry run anki-patcher <--flags> <command>
poetry run anki-patcher list: lists all available patch operationspoetry run anki-patcher patch -o <operation> -d <deck-name> -c <path-to-config.yml>: executes the specified operation on all cards of the deck
No need to install Python or Poetry. The container image includes all dependencies and downloads unidic at build time.
Quickstart with docker compose (recommended):
# List available operations
docker compose run --rm anki-patcher list
# Run an operation (example: gpt)
docker compose run --rm \
-e OPENROUTER_API_KEY=your_key \
anki-patcher patch -o gpt -d "Your::Deck" -c /work/example_configs/gpt_example.yml
Notes:
- Anki must be running on your Mac with the AnkiConnect add-on enabled.
- The container uses
ANKI_CONNECT_URL=http://host.docker.internal:8765by default so it can call your host's AnkiConnect. - Mount your configs via the provided
docker-compose.yml. By default,./configsand./example_configsare available at/work/configsand/work/example_configs. - Stock media operations (
add_image,add_tts) store files via AnkiConnect (storeMediaFile), so they work from Docker without mounting yourcollection.media. - If a custom operation writes media files directly, set
ANKI_MEDIA_FOLDERto a valid path reachable from the container and mount it. Two options:- Pass your host path directly (read/write):
-e ANKI_MEDIA_FOLDER="/absolute/host/path/to/collection.media"and mount that path into the container at the same location usingvolumes:. - Or mount into the container at
/anki_mediaand set-e ANKI_MEDIA_FOLDER=/anki_media.
- Pass your host path directly (read/write):
Example with media mount (only needed for custom operations that write directly to the media folder):
docker compose run --rm \
-e ANKI_MEDIA_FOLDER=/anki_media \
-v "$HOME/Library/Application Support/Anki2/User 1/collection.media":/anki_media:rw \
anki-patcher patch -o add_image -d "German::A1" -c /work/example_configs/add_image_example.yml
Optional environment variables:
- OPENROUTER_API_KEY or OPENAI_API_KEY (for
gptop) - OPENAI_API_BASE (defaults to https://openrouter.ai/api/v1)
- GOOGLE_API_KEY and CX (for
add_imageop)
add_image: for each card of a deck, adds an image retrieved from google, based on another card field value (example config)- use-case: add images to cards for language learning
- example-use:
poetry run anki-patcher patch -o add_image -d "German::A1" -c example_configs/add_image_example.yml
add_tts: for each card of a deck, adds a text-to-speech audio file retrieved from google tts, based on another card field value (example config)- use-case: add tts to cards for language learning
- example-use:
poetry run anki-patcher patch -o add_tts -d "German::A1" -c example_configs/add_tts_example.yml
gpt: for each card of a deck, adds a text generated by an OpenAI-compatible API (defaults to OpenRouter; e.g., openai/gpt-5), based on another card field value (example config)- use-case: add example sentences to cards
- example-use:
poetry run anki-patcher patch -o gpt -d "German::A1" -c example_configs/gpt_example.yml - env: set OPENROUTER_API_KEY (preferred) or OPENAI_API_KEY; optionally override OPENAI_API_BASE (defaults to https://openrouter.ai/api/v1)
replace: for each card of a deck, replaces a substring of a field value with another string (example config)- use-case: replace a substring of a field value with another string
- example-use:
poetry run anki-patcher patch -o replace -d "German::A1" -c example_configs/replace_example.yml
- ... building more, contributions welcome!
It's easy!
Just create a new file in operations with a execute(card_id, fields, config) function (see add_image for an example). Afterwards, the operation will be available to be executed via poetry run anki-patcher patch -o <operation> -d <deck-name> -c <path-to-config.yml> . To check if your operation is available, run poetry run anki-patcher list.