Anki python module

Hi, I am trying to write a simple python script that opens apkg aniki deck and remove some cards. but unfortunately, I even can’t even print a list of existing cards, does anybody have anki module samples? I used this one GitHub - ankitects/anki: Anki's shared backend and web components, and the Qt frontend

this is what I wrote:

import os
import zipfile
from anki.collection import Collection


# Extract the .apkg file
def extract_apkg(apkg_file):
    extract_dir = 'extracted_deck'
    with zipfile.ZipFile(apkg_file, 'r') as zip_ref:
        zip_ref.extractall(extract_dir)
    
    # Return the path to the collection.anki2 file
    return os.path.join(extract_dir, 'collection.anki2')


# Open the Anki collection
def open_collection(collection_path):
    col = Collection(collection_path)
    return col


# Remove cards that don't match 'Word = Hello'
def remove_non_matching_cards(col, word="Hello"):
    # Get all note IDs in the collection
    note_ids = col.find_notes('')
    
    for note_id in note_ids:
        note = col.get_note(note_id)
        word_in_note = note.fields[0]  # Assuming 'Word' is the first field
        print(note) # if I try to print the card, I have only one system card (or I don't know what it is), but the deck has many cards
        # If the word doesn't match, remove the note (and its cards)
        if word_in_note != word:
            col.remove(note)
            print(f"Removed note with ID: {note_id} (Word: {word_in_note})")


# Main function
def main(apkg_file):
    # Extract the .apkg file to get the collection.anki2 file
    collection_path = extract_apkg(apkg_file)
    print(f"Opened collection from: {collection_path}")

    # Open the collection
    col = open_collection(collection_path)

    # Remove all cards except the one with Word = 'Hello'
    remove_non_matching_cards(col)

    # Save the collection after changes
    col.save()
    print("Changes saved to the collection.")


if __name__ == "__main__":
    apkg_file = "english.apkg" 
    main(apkg_file)
1 Like

Maybe this should be .anki21 instead of .anki2?

See how anki’s own apkg importer gets the colpath on these lines:

1 Like

seems like I found a way to open apkg file and remove not needed cards(or notes in my case, not sure if it is a correct way):

from anki.collection import ImportAnkiPackageRequest, Collection

collection = Collection("collection.anki2")
collection.import_anki_package(
    ImportAnkiPackageRequest(
        package_path="4000.apkg"
    )
)

for c in collection.find_notes(""):
    note = collection.get_note(c)
    word = note.fields[0]
    # print(note.card_ids)
    # print(word)
    if word != "arch":
        collection.remove_notes([note.id])

but now how to save this collection as apkg deck please?

Well, if col.import_anki_package worked then maybe you can also use col.export_ankipackage too?

Something like this

from anki.collection import (
    ImportAnkiPackageRequest,
    Collection,
    ExportAnkiPackageOptions
)

# Edit notes in package using previous code, and then...

collection.export_ankipackage(
    "path/some/where/", 
    ExportAnkiPackageOptions(
        with_scheduling=True/False,
        with_deck_configs=True/False,
        legacy=True/False,
    )
)
class ExportAnkiPackageOptions(google.protobuf.message.Message):
    DESCRIPTOR: google.protobuf.descriptor.Descriptor

    WITH_SCHEDULING_FIELD_NUMBER: builtins.int
    WITH_DECK_CONFIGS_FIELD_NUMBER: builtins.int
    WITH_MEDIA_FIELD_NUMBER: builtins.int
    LEGACY_FIELD_NUMBER: builtins.int
    with_scheduling: builtins.bool
    with_deck_configs: builtins.bool
    with_media: builtins.bool
    legacy: builtins.bool
    def __init__(
        self,
        *,
        with_scheduling: builtins.bool = ...,
        with_deck_configs: builtins.bool = ...,
        with_media: builtins.bool = ...,
        legacy: builtins.bool = ...,
    ) -> None: ...

thank you, it works:

    collection.export_anki_package(
        out_path=output_path,
        limit=None,
        options=ExportAnkiPackageOptions(
            with_scheduling=False,
            with_deck_configs=False,
            with_media=True,
            legacy=False
        )
    )