Writes validated deck data as a legacy-compatible .apkg archive accepted by Anki 26.05.
More...
Static Public Member Functions | |
| static async Task | WriteAsync (AnkiDeck deck, string path, CancellationToken cancellationToken=default) |
| Writes one deck hierarchy to a package file without opening or modifying an Anki profile. | |
| static async Task | WriteAsync (AnkiPackage package, string path, CancellationToken cancellationToken=default) |
| Writes every hierarchy and all retained media in a previously read package to a package file. | |
| static async Task | WriteAsync (AnkiDeck deck, Stream destination, CancellationToken cancellationToken=default) |
| Writes one deck hierarchy to a writable caller-owned stream and leaves the stream open. | |
| static async Task | WriteAsync (AnkiPackage package, Stream destination, CancellationToken cancellationToken=default) |
| Writes every hierarchy and all retained media in a package to a caller-owned stream and leaves it open. | |
Writes validated deck data as a legacy-compatible .apkg archive accepted by Anki 26.05.
Choose a deck overload for a newly constructed hierarchy: it writes that root, its descendants, and media registered on those decks. Choose a package overload for read-modify-write: it writes every root and combines AnkiPackage.Media with deck media. Passing only package.Decks[0] intentionally omits other roots and package-only media.
Output contains collection.anki2 with schema-11 JSON metadata, a fixed scheduler-version-2 configuration, and a traditional numeric media map. Anki 26.05 was verified to import this representation. The writer does not emit native collection.anki21b, schema-18 protobuf metadata, or meta, and does not produce byte-identical Anki exports. A fixed default deck configuration is emitted; deck options, AnkiDeck.Metadata, AnkiDeck.UnknownData, and unsupported storage columns are not preserved. Review rows are written, but AnkiReviewLog.ReviewedAt is not independently encoded; AnkiReviewLog.Id becomes the legacy review-log key.
Every graph is validated before archive output begins. Identical case-sensitive media names with equal length and SHA-256 are coalesced; conflicting content is rejected. Path-backed media is rehashed while writing and rejected if it changed after registration. Do not mutate graphs, note types, cards, review histories, media collections, or backing files during a write. Separate calls have no shared mutable writer state and may run concurrently with separate inputs. Write timestamps make output semantically repeatable but not byte-for-byte deterministic.
Create a new deck and write it atomically to a path:
Preserve package-level media while modifying an existing package:
Definition at line 48 of file AnkiPackageWriter.cs.
|
inlinestatic |
Writes one deck hierarchy to a writable caller-owned stream and leaves the stream open.
| deck | The single root deck whose complete descendant hierarchy will be written. |
| destination | A writable stream positioned where the ZIP archive should begin. |
| cancellationToken | Cancels database creation and asynchronous archive or media I/O. |
The caller owns and must eventually dispose destination ; this method leaves it open on success or failure. Seeking is not required. Archive bytes begin at the current position, the original position is not restored, and existing content is not truncated. Before reusing a seekable stream, normally set Position = 0 and SetLength(0). Validation and media-collision checks occur before the first write. Failures after output begins can leave partial ZIP bytes, so use the path overload when transactional replacement matters. Do not access the stream concurrently.
| ArgumentNullException | deck or destination is null. |
| ArgumentException | destination is not writable. |
| ObjectDisposedException | destination or a path-backed media stream has been disposed. |
| AnkiValidationException | deck violates a checked domain invariant. |
| FileNotFoundException | A path-backed media source no longer exists. |
| DirectoryNotFoundException | A registered media directory does not exist. |
| UnauthorizedAccessException | A media source or the temporary workspace cannot be accessed. |
| InvalidDataException | A path-backed media payload no longer matches its registration-time SHA-256 digest. |
| Microsoft.Data.Sqlite.SqliteException | The temporary legacy collection database cannot be created or populated. |
| InvalidOperationException | Media filenames collide with different content, or mutable input changes during enumeration. |
| NotSupportedException | destination does not support a required write operation. |
| IOException | The destination, media, or temporary workspace cannot be read, written, finalized, or removed. |
| OperationCanceledException | cancellationToken is canceled. |
Definition at line 143 of file AnkiPackageWriter.cs.
|
inlinestatic |
Writes one deck hierarchy to a package file without opening or modifying an Anki profile.
| deck | The single root deck whose complete descendant hierarchy will be written. |
| path | The destination package path. The extension is not validated. |
| cancellationToken | Cancels database creation and asynchronous archive or media I/O. |
The graph and media-name collisions are validated before a destination or temporary output file is opened. The complete archive is built in a uniquely named file beside path , closed, then committed with one same-directory overwrite move. Until that move, an existing destination is never opened or truncated; a missing destination remains absent. Failures or cancellation before commit leave the previous destination unchanged and the temporary file is removed. The move provides filesystem rename atomicity, not a backup or a guarantee against power loss. Parent directories are not created, and the filename extension is not enforced.
| ArgumentNullException | deck or path is null. |
| ArgumentException | path is blank or has invalid path syntax. |
| PathTooLongException | path or a registered media path exceeds a platform path-length limit. |
| DirectoryNotFoundException | The destination parent or a registered media directory does not exist. |
| FileNotFoundException | A path-backed media source no longer exists. |
| UnauthorizedAccessException | The destination, a media source, or the temporary workspace cannot be accessed. |
| AnkiValidationException | deck violates a checked domain invariant. |
| InvalidDataException | A path-backed media payload no longer matches its registration-time SHA-256 digest. |
| Microsoft.Data.Sqlite.SqliteException | The temporary legacy collection database cannot be created or populated. |
| InvalidOperationException | Media filenames collide with different content, or mutable input changes during enumeration. |
| IOException | The destination, media, or temporary workspace cannot be read, written, finalized, replaced, or removed. |
| OperationCanceledException | cancellationToken is canceled. |
Definition at line 75 of file AnkiPackageWriter.cs.
|
inlinestatic |
Writes every hierarchy and all retained media in a package to a caller-owned stream and leaves it open.
| package | The package whose top-level deck hierarchies and package-level media will be written. |
| destination | A writable stream positioned where the ZIP archive should begin. |
| cancellationToken | Cancels database creation and asynchronous archive or media I/O. |
Use this overload for supported read-modify-write workflows. Every registration in AnkiPackage.Media is combined with media on all package deck hierarchies. The caller owns and must dispose destination ; the method leaves it open on success or failure. Seeking is not required. Output starts at the current position, existing content is not truncated, and the original position is not restored. Validation occurs before the first write, but database, media, cancellation, or I/O failure afterward can leave partial ZIP bytes. Use the path overload when an existing artifact must remain unchanged on failure.
| ArgumentNullException | package or destination is null. |
| ArgumentException | package contains no top-level decks, or destination is not writable. |
| ObjectDisposedException | destination or a path-backed media stream has been disposed. |
| AnkiValidationException | A hierarchy in package violates a checked domain invariant. |
| FileNotFoundException | A path-backed media source no longer exists. |
| DirectoryNotFoundException | A registered media directory does not exist. |
| UnauthorizedAccessException | A media source or the temporary workspace cannot be accessed. |
| InvalidDataException | A path-backed media payload no longer matches its registration-time SHA-256 digest. |
| Microsoft.Data.Sqlite.SqliteException | The temporary legacy collection database cannot be created or populated. |
| InvalidOperationException | Media filenames collide with different content, or mutable input changes during enumeration. |
| NotSupportedException | destination does not support a required write operation. |
| IOException | The destination, media, or temporary workspace cannot be read, written, finalized, or removed. |
| OperationCanceledException | cancellationToken is canceled. |
Definition at line 178 of file AnkiPackageWriter.cs.
|
inlinestatic |
Writes every hierarchy and all retained media in a previously read package to a package file.
| package | The package whose top-level deck hierarchies and package-level media will be written. |
| path | The destination package path. The extension is not validated. |
| cancellationToken | Cancels database creation and asynchronous archive or media I/O. |
Use this overload for supported read-modify-write workflows. It includes every registration in AnkiPackage.Media as well as registrations added to any deck in AnkiPackage.Decks. Identical duplicate registrations are coalesced; conflicting registrations and invalid graphs are rejected before output is opened. The complete archive is closed in a same-directory temporary file and committed with one overwrite move. Failure before commit leaves an existing destination unchanged, and temporary output is removed. This is lossless only for the explicitly supported fields described by AnkiPackageReader; unsupported Anki storage data discarded during reading cannot be recovered by this method.
| ArgumentNullException | package or path is null. |
| ArgumentException | package contains no top-level decks, or path is blank or invalid. |
| PathTooLongException | path or a registered media path exceeds a platform path-length limit. |
| DirectoryNotFoundException | The destination parent or a registered media directory does not exist. |
| FileNotFoundException | A path-backed media source no longer exists. |
| UnauthorizedAccessException | The destination, a media source, or the temporary workspace cannot be accessed. |
| AnkiValidationException | A hierarchy in package violates a checked domain invariant. |
| InvalidDataException | A path-backed media payload no longer matches its registration-time SHA-256 digest. |
| Microsoft.Data.Sqlite.SqliteException | The temporary legacy collection database cannot be created or populated. |
| InvalidOperationException | Media filenames collide with different content, or mutable input changes during enumeration. |
| IOException | The destination, media, or temporary workspace cannot be read, written, finalized, replaced, or removed. |
| OperationCanceledException | cancellationToken is canceled. |
Definition at line 109 of file AnkiPackageWriter.cs.