diff --git a/anki/exporting.py b/anki/exporting.py index 2ae5d40cd..3df18498e 100644 --- a/anki/exporting.py +++ b/anki/exporting.py @@ -4,7 +4,7 @@ import re, os, zipfile, shutil from anki.lang import _ -from anki.utils import ids2str, splitFields, json +from anki.utils import ids2str, splitFields, json, namedtmp from anki.hooks import runHook from anki import Collection @@ -113,6 +113,9 @@ class AnkiExporter(Exporter): Exporter.__init__(self, col) def exportInto(self, path): + # sched info+v2 scheduler not compatible w/ older clients + self._v2sched = self.col.schedVer() != 1 and self.includeSched + # create a new collection at the target try: os.unlink(path) @@ -263,13 +266,19 @@ class AnkiPackageExporter(AnkiExporter): z.close() def doExport(self, z, path): - if self.col.schedVer() != 1: - raise Exception("Experimental scheduler currently doesn't support deck exports.") - # export into the anki2 file colfile = path.replace(".apkg", ".anki2") AnkiExporter.exportInto(self, colfile) - z.write(colfile, "collection.anki2") + if not self._v2sched: + z.write(colfile, "collection.anki2") + else: + # fixme: remove in the future + raise Exception("Please switch to the normal scheduler before exporting a single deck with scheduling information.") + + # prevent older clients from accessing + self._addDummyCollection(z) + z.write(colfile, "collection.anki21") + # and media self.prepareMedia() media = self._exportMedia(z, self.mediaFiles, self.mediaDir) @@ -304,6 +313,20 @@ class AnkiPackageExporter(AnkiExporter): # is zipped up pass + # create a dummy collection to ensure older clients don't try to read + # data they don't understand + def _addDummyCollection(self, zip): + path = namedtmp("dummy.anki2") + c = Collection(path) + n = c.newNote() + n['Front'] = "This file requires a newer version of Anki." + c.addNote(n) + c.save() + c.close() + + zip.write(path, "collection.anki2") + os.unlink(path) + # Collection package ###################################################################### @@ -320,8 +343,13 @@ class AnkiCollectionPackageExporter(AnkiPackageExporter): def doExport(self, z, path): # close our deck & write it into the zip file, and reopen self.count = self.col.cardCount() + v2 = self.col.schedVer() != 1 self.col.close() - z.write(self.col.path, "collection.anki2") + if not v2: + z.write(self.col.path, "collection.anki2") + else: + self._addDummyCollection(z) + z.write(self.col.path, "collection.anki21") self.col.reopen() # copy all media if not self.includeMedia: diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index dfd4f1a0d..9510176ff 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -31,6 +31,9 @@ class Anki2Importer(Importer): self.src.close(save=False) def _prepareFiles(self): + if self.file.endswith(".anki21") and self.col.schedVer() == 1: + raise Exception("V2 scheduler must be enabled to import this file.") + self.dst = self.col self.src = Collection(self.file) diff --git a/anki/importing/apkg.py b/anki/importing/apkg.py index e31bc4b07..2e28e4400 100644 --- a/anki/importing/apkg.py +++ b/anki/importing/apkg.py @@ -12,8 +12,15 @@ class AnkiPackageImporter(Anki2Importer): def run(self): # extract the deck from the zip file self.zip = z = zipfile.ZipFile(self.file) - col = z.read("collection.anki2") - colpath = tmpfile(suffix=".anki2") + # v2 scheduler? + try: + z.getinfo("collection.anki21") + suffix = ".anki21" + except KeyError: + suffix = ".anki2" + + col = z.read("collection"+suffix) + colpath = tmpfile(suffix=suffix) with open(colpath, "wb") as f: f.write(col) self.file = colpath diff --git a/anki/storage.py b/anki/storage.py index b6ca539dc..afb382845 100644 --- a/anki/storage.py +++ b/anki/storage.py @@ -40,6 +40,8 @@ def Collection(path, lock=True, server=False, sync=True, log=False): col = _Collection(db, server, log) if ver < SCHEMA_VERSION: _upgrade(col, ver) + elif ver > SCHEMA_VERSION: + raise Exception("This file requires a newer version of Anki.") elif create: # add in reverse order so basic is default addClozeModel(col) diff --git a/aqt/importing.py b/aqt/importing.py index 499beb857..de45cc6be 100644 --- a/aqt/importing.py +++ b/aqt/importing.py @@ -388,10 +388,18 @@ def replaceWithApkg(mw, file, backup): def _replaceWithApkg(mw, file, backup): mw.progress.start(immediate=True) - # overwrite collection + z = zipfile.ZipFile(file) + + # v2 scheduler? + colname = "collection.anki21" try: - z.extract("collection.anki2", mw.pm.profileFolder()) + z.getinfo(colname) + except KeyError: + colname = "collection.anki2" + + try: + z.extract(colname, mw.pm.profileFolder()) except: mw.progress.finish() showWarning(_("The provided file is not a valid .apkg file."))