mirror of
https://github.com/ankitects/anki.git
synced 2025-09-23 00:12:25 -04:00
zip64 support, and import/export improvements
- we now allow exports over 2gb/64k files - AnkiMobile and AnkiDroid will need to be updated to support this - avoid compressing media files in export, as in the common case of jpg/mp3 it's much faster with no increase in size - exports and imports now show # of files processed - mw.progress.update() now limits # of updates per sec
This commit is contained in:
parent
818c4534e9
commit
3bbd0bca7e
4 changed files with 35 additions and 12 deletions
|
@ -248,7 +248,7 @@ class AnkiPackageExporter(AnkiExporter):
|
||||||
|
|
||||||
def exportInto(self, path):
|
def exportInto(self, path):
|
||||||
# open a zip file
|
# open a zip file
|
||||||
z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED)
|
z = zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED, allowZip64=True)
|
||||||
# if all decks and scheduling included, full export
|
# if all decks and scheduling included, full export
|
||||||
if self.includeSched and not self.did:
|
if self.includeSched and not self.did:
|
||||||
media = self.exportVerbatim(z)
|
media = self.exportVerbatim(z)
|
||||||
|
@ -268,11 +268,12 @@ class AnkiPackageExporter(AnkiExporter):
|
||||||
self.prepareMedia()
|
self.prepareMedia()
|
||||||
media = {}
|
media = {}
|
||||||
for c, file in enumerate(self.mediaFiles):
|
for c, file in enumerate(self.mediaFiles):
|
||||||
c = str(c)
|
cStr = str(c)
|
||||||
mpath = os.path.join(self.mediaDir, file)
|
mpath = os.path.join(self.mediaDir, file)
|
||||||
if os.path.exists(mpath):
|
if os.path.exists(mpath):
|
||||||
z.write(mpath, c)
|
z.write(mpath, cStr, zipfile.ZIP_STORED)
|
||||||
media[c] = file
|
media[cStr] = file
|
||||||
|
runHook("exportedMediaFiles", c)
|
||||||
# tidy up intermediate files
|
# tidy up intermediate files
|
||||||
os.unlink(colfile)
|
os.unlink(colfile)
|
||||||
p = path.replace(".apkg", ".media.db2")
|
p = path.replace(".apkg", ".media.db2")
|
||||||
|
@ -294,11 +295,13 @@ class AnkiPackageExporter(AnkiExporter):
|
||||||
media = {}
|
media = {}
|
||||||
mdir = self.col.media.dir()
|
mdir = self.col.media.dir()
|
||||||
for c, file in enumerate(os.listdir(mdir)):
|
for c, file in enumerate(os.listdir(mdir)):
|
||||||
c = str(c)
|
cStr = str(c)
|
||||||
mpath = os.path.join(mdir, file)
|
mpath = os.path.join(mdir, file)
|
||||||
if os.path.exists(mpath):
|
if os.path.exists(mpath):
|
||||||
z.write(mpath, c)
|
z.write(mpath, cStr, zipfile.ZIP_STORED)
|
||||||
media[c] = file
|
media[cStr] = file
|
||||||
|
runHook("exportedMediaFiles", c)
|
||||||
|
|
||||||
return media
|
return media
|
||||||
|
|
||||||
def prepareMedia(self):
|
def prepareMedia(self):
|
||||||
|
|
|
@ -9,6 +9,9 @@ import aqt
|
||||||
from aqt.utils import getSaveFile, tooltip, showWarning, askUser, \
|
from aqt.utils import getSaveFile, tooltip, showWarning, askUser, \
|
||||||
checkInvalidFilename
|
checkInvalidFilename
|
||||||
from anki.exporting import exporters
|
from anki.exporting import exporters
|
||||||
|
from anki.hooks import addHook, remHook
|
||||||
|
from anki.lang import ngettext
|
||||||
|
|
||||||
|
|
||||||
class ExportDialog(QDialog):
|
class ExportDialog(QDialog):
|
||||||
|
|
||||||
|
@ -105,7 +108,13 @@ class ExportDialog(QDialog):
|
||||||
showWarning(_("Couldn't save file: %s") % unicode(e))
|
showWarning(_("Couldn't save file: %s") % unicode(e))
|
||||||
else:
|
else:
|
||||||
os.unlink(file)
|
os.unlink(file)
|
||||||
|
exportedMedia = lambda cnt: self.mw.progress.update(
|
||||||
|
label=ngettext("Exported %d media file",
|
||||||
|
"Exported %d media files", cnt) % cnt
|
||||||
|
)
|
||||||
|
addHook("exportedMediaFiles", exportedMedia)
|
||||||
self.exporter.exportInto(file)
|
self.exporter.exportInto(file)
|
||||||
|
remHook("exportedMediaFiles", exportedMedia)
|
||||||
if verbatim:
|
if verbatim:
|
||||||
if usingHomedir:
|
if usingHomedir:
|
||||||
msg = _("A file called %s was saved in your home directory.")
|
msg = _("A file called %s was saved in your home directory.")
|
||||||
|
|
|
@ -16,6 +16,8 @@ from anki.hooks import addHook, remHook
|
||||||
import aqt.forms
|
import aqt.forms
|
||||||
import aqt.modelchooser
|
import aqt.modelchooser
|
||||||
import aqt.deckchooser
|
import aqt.deckchooser
|
||||||
|
from anki.lang import ngettext
|
||||||
|
|
||||||
|
|
||||||
class ChangeMap(QDialog):
|
class ChangeMap(QDialog):
|
||||||
def __init__(self, mw, model, current):
|
def __init__(self, mw, model, current):
|
||||||
|
@ -391,8 +393,16 @@ def replaceWithApkg(mw, file, backup):
|
||||||
# unwanted media. in the future we might also want to deduplicate this
|
# unwanted media. in the future we might also want to deduplicate this
|
||||||
# step
|
# step
|
||||||
d = os.path.join(mw.pm.profileFolder(), "collection.media")
|
d = os.path.join(mw.pm.profileFolder(), "collection.media")
|
||||||
for c, file in json.loads(z.read("media")).items():
|
for n, (cStr, file) in enumerate(json.loads(z.read("media")).items()):
|
||||||
open(os.path.join(d, file), "wb").write(z.read(str(c)))
|
mw.progress.update(ngettext("Processed %d media file",
|
||||||
|
"Processed %d media files", n) % n)
|
||||||
|
size = z.getinfo(cStr).file_size
|
||||||
|
dest = os.path.join(d, file)
|
||||||
|
# if we have a matching file size
|
||||||
|
if os.path.exists(dest) and size == os.stat(dest).st_size:
|
||||||
|
continue
|
||||||
|
data = z.read(cStr)
|
||||||
|
open(dest, "wb").write(data)
|
||||||
z.close()
|
z.close()
|
||||||
# reload
|
# reload
|
||||||
mw.loadCollection()
|
mw.loadCollection()
|
||||||
|
|
|
@ -110,21 +110,22 @@ Your pysqlite2 is too old. Anki will appear frozen during long operations."""
|
||||||
self._min = min
|
self._min = min
|
||||||
self._max = max
|
self._max = max
|
||||||
self._firstTime = time.time()
|
self._firstTime = time.time()
|
||||||
self._lastTime = time.time()
|
self._lastUpdate = time.time()
|
||||||
self._disabled = False
|
self._disabled = False
|
||||||
|
|
||||||
def update(self, label=None, value=None, process=True, maybeShow=True):
|
def update(self, label=None, value=None, process=True, maybeShow=True):
|
||||||
#print self._min, self._counter, self._max, label, time.time() - self._lastTime
|
#print self._min, self._counter, self._max, label, time.time() - self._lastTime
|
||||||
if maybeShow:
|
if maybeShow:
|
||||||
self._maybeShow()
|
self._maybeShow()
|
||||||
self._lastTime = time.time()
|
elapsed = time.time() - self._lastUpdate
|
||||||
if label:
|
if label:
|
||||||
self._win.setLabelText(label)
|
self._win.setLabelText(label)
|
||||||
if self._max and self._shown:
|
if self._max and self._shown:
|
||||||
self._counter = value or (self._counter+1)
|
self._counter = value or (self._counter+1)
|
||||||
self._win.setValue(self._counter)
|
self._win.setValue(self._counter)
|
||||||
if process:
|
if process and elapsed >= 0.2:
|
||||||
self.app.processEvents(QEventLoop.ExcludeUserInputEvents)
|
self.app.processEvents(QEventLoop.ExcludeUserInputEvents)
|
||||||
|
self._lastUpdate = time.time()
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
self._levels -= 1
|
self._levels -= 1
|
||||||
|
|
Loading…
Reference in a new issue