2019-02-05 04:59:03 +01:00
|
|
|
# Copyright: Ankitects Pty Ltd and contributors
|
2012-12-21 08:51:59 +01:00
|
|
|
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
2020-02-25 08:38:49 +01:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
import os
|
2013-05-19 17:34:55 +02:00
|
|
|
import re
|
2019-12-20 10:19:03 +01:00
|
|
|
import time
|
2020-03-06 05:19:25 +01:00
|
|
|
from concurrent.futures import Future
|
2020-02-09 07:37:50 +01:00
|
|
|
from typing import List, Optional
|
2013-05-19 17:34:55 +02:00
|
|
|
|
2019-12-20 10:19:03 +01:00
|
|
|
import aqt
|
2020-01-15 04:49:26 +01:00
|
|
|
from anki import hooks
|
2020-05-04 05:23:08 +02:00
|
|
|
from anki.exporting import Exporter, exporters
|
2019-12-20 10:19:03 +01:00
|
|
|
from anki.lang import _, ngettext
|
|
|
|
from aqt.qt import *
|
2020-11-17 08:42:43 +01:00
|
|
|
from aqt.utils import TR, checkInvalidFilename, getSaveFile, showWarning, tooltip, tr
|
2019-12-20 10:19:03 +01:00
|
|
|
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
class ExportDialog(QDialog):
|
2020-02-25 08:56:46 +01:00
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
mw: aqt.main.AnkiQt,
|
|
|
|
did: Optional[int] = None,
|
|
|
|
cids: Optional[List[int]] = None,
|
|
|
|
):
|
2012-12-21 08:51:59 +01:00
|
|
|
QDialog.__init__(self, mw, Qt.Window)
|
|
|
|
self.mw = mw
|
2020-03-06 05:03:23 +01:00
|
|
|
self.col = mw.col.weakref()
|
2012-12-21 08:51:59 +01:00
|
|
|
self.frm = aqt.forms.exporting.Ui_ExportDialog()
|
|
|
|
self.frm.setupUi(self)
|
2020-05-04 05:23:08 +02:00
|
|
|
self.exporter: Optional[Exporter] = None
|
2020-02-09 07:37:50 +01:00
|
|
|
self.cids = cids
|
2014-06-20 02:13:12 +02:00
|
|
|
self.setup(did)
|
2012-12-21 08:51:59 +01:00
|
|
|
self.exec_()
|
|
|
|
|
2020-02-09 07:37:50 +01:00
|
|
|
def setup(self, did: Optional[int]):
|
2017-09-10 08:58:55 +02:00
|
|
|
self.exporters = exporters()
|
|
|
|
# if a deck specified, start with .apkg type selected
|
|
|
|
idx = 0
|
2020-02-09 07:19:37 +01:00
|
|
|
if did or self.cids:
|
2019-12-23 01:34:10 +01:00
|
|
|
for c, (k, e) in enumerate(self.exporters):
|
2017-09-10 08:58:55 +02:00
|
|
|
if e.ext == ".apkg":
|
|
|
|
idx = c
|
|
|
|
break
|
|
|
|
self.frm.format.insertItems(0, [e[0] for e in self.exporters])
|
|
|
|
self.frm.format.setCurrentIndex(idx)
|
2020-05-04 05:23:08 +02:00
|
|
|
qconnect(self.frm.format.activated, self.exporterChanged)
|
2017-09-10 08:58:55 +02:00
|
|
|
self.exporterChanged(idx)
|
|
|
|
# deck list
|
2020-02-09 07:26:11 +01:00
|
|
|
if self.cids is None:
|
2020-11-17 08:42:43 +01:00
|
|
|
self.decks = [tr(TR.EXPORTING_ALL_DECKS)]
|
2020-05-15 13:21:10 +02:00
|
|
|
self.decks.extend(d.name for d in self.col.decks.all_names_and_ids())
|
2020-02-09 07:26:11 +01:00
|
|
|
else:
|
2020-11-17 08:42:43 +01:00
|
|
|
self.decks = [tr(TR.EXPORTING_SELECTED_NOTES)]
|
2012-12-21 08:51:59 +01:00
|
|
|
self.frm.deck.addItems(self.decks)
|
|
|
|
# save button
|
2020-11-17 08:42:43 +01:00
|
|
|
b = QPushButton(tr(TR.EXPORTING_EXPORT))
|
2012-12-21 08:51:59 +01:00
|
|
|
self.frm.buttonBox.addButton(b, QDialogButtonBox.AcceptRole)
|
2014-06-20 02:13:12 +02:00
|
|
|
# set default option if accessed through deck button
|
|
|
|
if did:
|
2019-12-23 01:34:10 +01:00
|
|
|
name = self.mw.col.decks.get(did)["name"]
|
2014-06-20 02:13:12 +02:00
|
|
|
index = self.frm.deck.findText(name)
|
|
|
|
self.frm.deck.setCurrentIndex(index)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
def exporterChanged(self, idx):
|
2017-09-10 08:58:55 +02:00
|
|
|
self.exporter = self.exporters[idx][1](self.col)
|
|
|
|
self.isApkg = self.exporter.ext == ".apkg"
|
|
|
|
self.isVerbatim = getattr(self.exporter, "verbatim", False)
|
2020-08-23 05:48:20 +02:00
|
|
|
self.isTextNote = getattr(self.exporter, "includeTags", False)
|
2017-09-10 08:58:55 +02:00
|
|
|
self.frm.includeSched.setVisible(
|
2019-12-23 01:34:10 +01:00
|
|
|
getattr(self.exporter, "includeSched", None) is not None
|
|
|
|
)
|
2017-09-10 08:58:55 +02:00
|
|
|
self.frm.includeMedia.setVisible(
|
2019-12-23 01:34:10 +01:00
|
|
|
getattr(self.exporter, "includeMedia", None) is not None
|
|
|
|
)
|
2013-04-15 06:46:07 +02:00
|
|
|
self.frm.includeTags.setVisible(
|
2019-12-23 01:34:10 +01:00
|
|
|
getattr(self.exporter, "includeTags", None) is not None
|
|
|
|
)
|
2019-03-04 23:57:53 +01:00
|
|
|
html = getattr(self.exporter, "includeHTML", None)
|
|
|
|
if html is not None:
|
|
|
|
self.frm.includeHTML.setVisible(True)
|
|
|
|
self.frm.includeHTML.setChecked(html)
|
|
|
|
else:
|
|
|
|
self.frm.includeHTML.setVisible(False)
|
2017-09-10 08:58:55 +02:00
|
|
|
# show deck list?
|
|
|
|
self.frm.deck.setVisible(not self.isVerbatim)
|
2012-12-21 08:51:59 +01:00
|
|
|
|
|
|
|
def accept(self):
|
2019-12-23 01:34:10 +01:00
|
|
|
self.exporter.includeSched = self.frm.includeSched.isChecked()
|
|
|
|
self.exporter.includeMedia = self.frm.includeMedia.isChecked()
|
|
|
|
self.exporter.includeTags = self.frm.includeTags.isChecked()
|
|
|
|
self.exporter.includeHTML = self.frm.includeHTML.isChecked()
|
2020-02-09 07:26:11 +01:00
|
|
|
idx = self.frm.deck.currentIndex()
|
|
|
|
if self.cids is not None:
|
|
|
|
# Browser Selection
|
|
|
|
self.exporter.cids = self.cids
|
|
|
|
self.exporter.did = None
|
|
|
|
elif idx == 0:
|
|
|
|
# All decks
|
2012-12-21 08:51:59 +01:00
|
|
|
self.exporter.did = None
|
2020-02-09 07:26:11 +01:00
|
|
|
self.exporter.cids = None
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
2020-02-09 07:26:11 +01:00
|
|
|
# Deck idx-1 in the list of decks
|
|
|
|
self.exporter.cids = None
|
2012-12-21 08:51:59 +01:00
|
|
|
name = self.decks[self.frm.deck.currentIndex()]
|
|
|
|
self.exporter.did = self.col.decks.id(name)
|
2017-09-10 08:58:55 +02:00
|
|
|
if self.isVerbatim:
|
2019-12-23 01:34:10 +01:00
|
|
|
name = time.strftime("-%Y-%m-%d@%H-%M-%S", time.localtime(time.time()))
|
2020-11-17 08:42:43 +01:00
|
|
|
deck_name = tr(TR.EXPORTING_COLLECTION) + name
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
2013-05-19 17:34:55 +02:00
|
|
|
# Get deck name and remove invalid filename characters
|
2013-05-18 18:24:53 +02:00
|
|
|
deck_name = self.decks[self.frm.deck.currentIndex()]
|
2019-12-23 01:34:10 +01:00
|
|
|
deck_name = re.sub('[\\\\/?<>:*|"^]', "_", deck_name)
|
2017-09-10 08:58:55 +02:00
|
|
|
|
2019-12-23 01:34:10 +01:00
|
|
|
filename = "{0}{1}".format(deck_name, self.exporter.ext)
|
2020-08-26 00:33:35 +02:00
|
|
|
if callable(self.exporter.key):
|
|
|
|
key_str = self.exporter.key()
|
|
|
|
else:
|
|
|
|
key_str = self.exporter.key
|
2017-09-10 08:58:55 +02:00
|
|
|
while 1:
|
2019-12-23 01:34:10 +01:00
|
|
|
file = getSaveFile(
|
2020-08-31 05:29:28 +02:00
|
|
|
self,
|
2020-11-17 08:42:43 +01:00
|
|
|
tr(TR.ACTIONS_EXPORT),
|
2020-08-31 05:29:28 +02:00
|
|
|
"export",
|
|
|
|
key_str,
|
|
|
|
self.exporter.ext,
|
|
|
|
fname=filename,
|
2019-12-23 01:34:10 +01:00
|
|
|
)
|
2017-09-10 08:58:55 +02:00
|
|
|
if not file:
|
|
|
|
return
|
|
|
|
if checkInvalidFilename(os.path.basename(file), dirsep=False):
|
|
|
|
continue
|
2020-02-25 08:38:49 +01:00
|
|
|
if os.path.commonprefix([self.mw.pm.base, file]) == self.mw.pm.base:
|
|
|
|
showWarning("Please choose a different export location.")
|
|
|
|
continue
|
2017-09-10 08:58:55 +02:00
|
|
|
break
|
2012-12-21 08:51:59 +01:00
|
|
|
self.hide()
|
|
|
|
if file:
|
2020-03-06 05:19:25 +01:00
|
|
|
# check we can write to file
|
2012-12-21 08:51:59 +01:00
|
|
|
try:
|
|
|
|
f = open(file, "wb")
|
|
|
|
f.close()
|
2016-05-12 06:45:35 +02:00
|
|
|
except (OSError, IOError) as e:
|
2020-11-17 08:42:43 +01:00
|
|
|
showWarning(tr(TR.EXPORTING_COULDNT_SAVE_FILE, val="%s") % str(e))
|
2012-12-21 08:51:59 +01:00
|
|
|
else:
|
|
|
|
os.unlink(file)
|
2020-03-06 05:19:25 +01:00
|
|
|
|
|
|
|
# progress handler
|
|
|
|
def exported_media(cnt):
|
|
|
|
self.mw.taskman.run_on_main(
|
|
|
|
lambda: self.mw.progress.update(
|
|
|
|
label=ngettext(
|
|
|
|
"Exported %d media file", "Exported %d media files", cnt
|
|
|
|
)
|
|
|
|
% cnt
|
2019-12-23 01:34:10 +01:00
|
|
|
)
|
|
|
|
)
|
2020-03-06 05:19:25 +01:00
|
|
|
|
|
|
|
def do_export():
|
2012-12-21 08:51:59 +01:00
|
|
|
self.exporter.exportInto(file)
|
2020-03-06 05:19:25 +01:00
|
|
|
|
|
|
|
def on_done(future: Future):
|
2012-12-21 08:51:59 +01:00
|
|
|
self.mw.progress.finish()
|
2020-03-06 05:19:25 +01:00
|
|
|
hooks.media_files_did_export.remove(exported_media)
|
|
|
|
# raises if exporter failed
|
|
|
|
future.result()
|
|
|
|
self.on_export_finished()
|
|
|
|
|
2020-05-31 03:24:33 +02:00
|
|
|
self.mw.progress.start()
|
2020-03-06 05:19:25 +01:00
|
|
|
hooks.media_files_did_export.append(exported_media)
|
|
|
|
|
|
|
|
self.mw.taskman.run_in_background(do_export, on_done)
|
|
|
|
|
|
|
|
def on_export_finished(self):
|
|
|
|
if self.isVerbatim:
|
2020-11-17 08:42:43 +01:00
|
|
|
msg = tr(TR.EXPORTING_COLLECTION_EXPORTED)
|
2020-03-06 05:19:25 +01:00
|
|
|
self.mw.reopen()
|
|
|
|
else:
|
|
|
|
if self.isTextNote:
|
|
|
|
msg = (
|
|
|
|
ngettext(
|
2020-08-31 05:29:28 +02:00
|
|
|
"%d note exported.",
|
|
|
|
"%d notes exported.",
|
|
|
|
self.exporter.count,
|
2020-03-06 05:19:25 +01:00
|
|
|
)
|
|
|
|
% self.exporter.count
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
msg = (
|
|
|
|
ngettext(
|
2020-08-31 05:29:28 +02:00
|
|
|
"%d card exported.",
|
|
|
|
"%d cards exported.",
|
|
|
|
self.exporter.count,
|
2020-03-06 05:19:25 +01:00
|
|
|
)
|
|
|
|
% self.exporter.count
|
|
|
|
)
|
|
|
|
tooltip(msg, period=3000)
|
|
|
|
QDialog.reject(self)
|