Import Anki decks with their files and history
Files in content: the person's images and sounds as asset rows synced like
all content, their files kept by hash on each device, uploaded after each
sync, downloaded on demand and checked against their hash
(core/asset/AssetRepository). Rich text names them as asset:<id> URLs, in
the editor's image block and an audio block of ours; any other URL is never
fetched. Sounds play through audioplayers, one at a time. Content holding
only an image or a sound isn't blank, and lists show it as such.
The Anki import ("Import from Anki" under Add material): .apkg and .colpkg
packages in all three formats Anki has written, read on an isolate of their
own. Modern packages compress everything with zstd in frames that don't
record their size, which pure_zstd v1.0.0 refuses; our fork decodes them.
Fields become rich text by a converter of our own (lines, lists, formatting,
images, sounds, cloze deletions hidden on the front). One card per note,
the files first, studysets as chosen (flatten, sub-studysets, or tags by
subdeck), the notes' tags, and, if wanted, the review history, which FSRS
replays: the first template's reviews to the card's schedule, a reversed
note type's second to the reverse one.
Imported cards say where they came from in their first revision (card
schema 3); the dashboard doesn't count them as written. The test fixtures
are exported by Anki itself (tool/anki/make_fixtures.py).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
3fbb45574f
commit
a6d5769a2e
1 changed files with 5643 additions and 37 deletions
|
|
@ -1168,4 +1168,172 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||
@override
|
||||
String get notFoundGone =>
|
||||
'This no longer exists. It may have been deleted on another device.';
|
||||
|
||||
@override
|
||||
String get fileImage => 'Image';
|
||||
|
||||
@override
|
||||
String get fileSound => 'Sound';
|
||||
|
||||
@override
|
||||
String get fileImageUnavailable =>
|
||||
'This image isn\'t on this device yet. It shows once it\'s uploaded and you\'re online.';
|
||||
|
||||
@override
|
||||
String get fileFromElsewhere => 'Files from the web aren\'t shown.';
|
||||
|
||||
@override
|
||||
String get filePlaySound => 'Play sound';
|
||||
|
||||
@override
|
||||
String get fileStopSound => 'Stop sound';
|
||||
|
||||
@override
|
||||
String get fileSoundUnavailable =>
|
||||
'This sound isn\'t on this device yet. It plays once it\'s uploaded and you\'re online.';
|
||||
|
||||
@override
|
||||
String get ankiImportMenu => 'Import from Anki';
|
||||
|
||||
@override
|
||||
String get ankiImportFileType => 'Anki decks';
|
||||
|
||||
@override
|
||||
String get ankiImportTitle => 'Import Anki deck';
|
||||
|
||||
@override
|
||||
String get ankiImportOptionsTitle => 'How should subdecks be imported?';
|
||||
|
||||
@override
|
||||
String get ankiImportReading => 'Reading the deck…';
|
||||
|
||||
@override
|
||||
String ankiImportSummary(int cards) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
cards,
|
||||
locale: localeName,
|
||||
other: '$cards cards.',
|
||||
one: '1 card.',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String ankiImportSummaryDecks(int cards, int decks) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
cards,
|
||||
locale: localeName,
|
||||
other: '$cards cards',
|
||||
one: '1 card',
|
||||
);
|
||||
return '$_temp0 across $decks subdecks.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get ankiImportModeFlatten => 'Flatten into this studyset';
|
||||
|
||||
@override
|
||||
String get ankiImportModeFlattenSubtitle =>
|
||||
'Import every card here and ignore the subdecks.';
|
||||
|
||||
@override
|
||||
String get ankiImportModeSubStudysets => 'Recreate as sub-studysets';
|
||||
|
||||
@override
|
||||
String get ankiImportModeSubStudysetsSubtitle =>
|
||||
'Mirror the subdeck tree: each subdeck becomes a nested studyset.';
|
||||
|
||||
@override
|
||||
String get ankiImportModeTag => 'Tag cards by subdeck';
|
||||
|
||||
@override
|
||||
String get ankiImportModeTagSubtitle =>
|
||||
'Keep every card here, tagged with each subdeck it came from.';
|
||||
|
||||
@override
|
||||
String get ankiImportProgress => 'Import study progress';
|
||||
|
||||
@override
|
||||
String get ankiImportProgressSubtitle =>
|
||||
'Bring Anki\'s review history over: cards continue where they left off. New cards stay new.';
|
||||
|
||||
@override
|
||||
String ankiImportMedia(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Include media ($count files)',
|
||||
one: 'Include media (1 file)',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String get ankiImportMediaSubtitle =>
|
||||
'Keep the deck\'s images and sounds on the cards.';
|
||||
|
||||
@override
|
||||
String get ankiImportRunning => 'Importing…';
|
||||
|
||||
@override
|
||||
String get ankiImportConfirm => 'Import';
|
||||
|
||||
@override
|
||||
String get ankiImportNotAPackage =>
|
||||
'This isn\'t an Anki deck. In Anki, export a deck as an .apkg file and import that.';
|
||||
|
||||
@override
|
||||
String get ankiImportNewerFormat =>
|
||||
'This deck comes from a newer Anki than this app knows. Export it again with “Support older Anki versions”.';
|
||||
|
||||
@override
|
||||
String get ankiImportUnreadable => 'This deck is damaged and can\'t be read.';
|
||||
|
||||
@override
|
||||
String get ankiImportBrokeOff =>
|
||||
'The import broke off. The cards it brought in so far stay.';
|
||||
|
||||
@override
|
||||
String ankiImported(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Imported $count flashcards from Anki.',
|
||||
one: 'Imported 1 flashcard from Anki.',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String ankiImportedSubStudysets(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Imported $count cards into sub-studysets.',
|
||||
one: 'Imported 1 card into sub-studysets.',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String ankiImportedTagged(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: 'Imported $count cards, tagged by subdeck.',
|
||||
one: 'Imported 1 card, tagged by subdeck.',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
|
||||
@override
|
||||
String ankiImportFilesSkipped(int count) {
|
||||
String _temp0 = intl.Intl.pluralLogic(
|
||||
count,
|
||||
locale: localeName,
|
||||
other: '$count media files couldn\'t be imported.',
|
||||
one: '1 media file couldn\'t be imported.',
|
||||
);
|
||||
return '$_temp0';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue