[ankipack] TypeScript library for creating, reading and editing .apkg files

TL;DR: I built this because I needed it and could not find it. Hope it helps you :slight_smile:

I have a browser extension that exports Quizlet sets to Anki including images and audio, so I needed to create .apkg files inside the extension. The ones I could find build Anki packages fine, but they write the legacy schema 11 layout and I wanted the current one. So I thought, how hard can it be. Longer rabbit hole than expected…

The first versions worked but were not good. Over the last few weeks I added unit tests and an e2e suite that runs Anki’s own Rust core over the files it writes. That turned up a lot of bugs and I fixed them. It now also refuses input Anki would quietly rewrite on import, and it reads existing packages, so you can open one, change it, and write it back with the rest intact.

There are probably still bugs, nobody ships without them, but I am comfortable relying on it, and it will keep being maintained because my extension depends on it.

If you make Anki decks from JavaScript or TypeScript, give it a try and tell me what breaks.

Nice, I write .apkg from TypeScript in the browser too (a converter for the same Quizlet problem, plus a generator).
I went the opposite way on format and stayed on the legacy schema 11 collection.anki2 on purpose. It is what every client imports, including old AnkiDroid builds people never update, and it needs nothing beyond sql.js and a zip writer. The manual’s own wording for the modern format is that the file “will not be readable by older Anki clients”. For a library behind a browser extension that’s a support-ticket generator, so I’d be curious whether you plan a legacy fallback, or whether you consider the old clients gone by now.

One thing I learned: media file names in the media manifest, not the [sound:...] tag in the field, are what Anki trusts on import, and a mismatch is silent, the card just plays nothing.

Cool, what’s the name of the extension or repo that converts the Quizlet cards? Mine is called QuickCards, and you can just download it from the Chrome Web Store. Would love to hear some feedback on that one as well!

Regarding the schema choice. It basically boils down to two facts. Firstly, I just wanted the new schema for future-proofing, and nothing that satisfied me existed. Secondly, the new schema 18 is now supported for 5 years, and I, personally, don’t see a reason why someone wouldn’t update Anki in that period. Though if there is a legitimate reason, I might add the legacy format as an export option to AnkiPack.

Now to your media thing. Yeah, it’s an issue where you think it shouldn’t be that hard to solve. Currently AnkiPack doesn’t warn you on a mismatch. Though maybe I could make it so the text gets scanned and warns you about potential mismatches. I don’t think a hard error would be good here. But I’m not sure yet.

Mine isn’t an extension, it’s a page on my site: Quizlet to Anki converter | NextLang. Runs fully in the browser, nothing gets uploaded, no account. I’ll install QuickCards and check it too. Thanks

On the schema, that’s a fair answer. Five years is five years. My reason for staying on 11 isn’t Anki desktop, it’s who’s on the other end. My users are language learners, not Anki people. A lot of them are on “whatever AnkiDroid” build shipped with their phone and when an import fails they don’t file a bug. Schema 11 costs me nothing and removes that whole class of email.

On the media warning: warn, don’t error, agreed. One thing to watch when you write the check, don’t require a one to one match. I dedupe audio, so the same mp3 is referenced by a bunch of notes and shows up once in the manifest. A naive “every manifest entry referenced exactly once” rule would light up on a perfectly healthy deck. The direction that matters is field to manifest. A [sound:x.mp3] with no x.mp3 in the manifest is silent, and nothing anywhere tells the user.

What made it a non-issue on my side was building the tag and the manifest entry from the same variable, instead of two code paths that agree by convention.