update auto-sync code
This commit is contained in:
parent
058ff1b71a
commit
7e221f0acf
@ -42,6 +42,7 @@ from aqt.mediasync import MediaSyncer
|
||||
from aqt.profiles import ProfileManager as ProfileManagerType
|
||||
from aqt.qt import *
|
||||
from aqt.qt import sip
|
||||
from aqt.sync import sync_collection, sync_login
|
||||
from aqt.taskman import TaskManager
|
||||
from aqt.theme import theme_manager
|
||||
from aqt.utils import (
|
||||
@ -380,13 +381,9 @@ close the profile or restart Anki."""
|
||||
self.taskman.run_in_background(downgrade, on_done)
|
||||
|
||||
def loadProfile(self, onsuccess: Optional[Callable] = None) -> None:
|
||||
self.maybeAutoSync()
|
||||
|
||||
if not self.loadCollection():
|
||||
return
|
||||
|
||||
self.maybe_auto_sync_media()
|
||||
|
||||
self.pm.apply_profile_options()
|
||||
|
||||
# show main window
|
||||
@ -408,17 +405,16 @@ close the profile or restart Anki."""
|
||||
self.handleImport(self.pendingImport)
|
||||
self.pendingImport = None
|
||||
gui_hooks.profile_did_open()
|
||||
if onsuccess:
|
||||
onsuccess()
|
||||
|
||||
if onsuccess is None:
|
||||
onsuccess = lambda: None
|
||||
self.maybe_auto_sync_on_open_close(onsuccess)
|
||||
|
||||
def unloadProfile(self, onsuccess: Callable) -> None:
|
||||
def callback():
|
||||
self._unloadProfile()
|
||||
onsuccess()
|
||||
|
||||
# start media sync if not already running
|
||||
self.maybe_auto_sync_media()
|
||||
|
||||
gui_hooks.profile_will_close()
|
||||
self.unloadCollection(callback)
|
||||
|
||||
@ -433,8 +429,6 @@ close the profile or restart Anki."""
|
||||
# at this point there should be no windows left
|
||||
self._checkForUnclosedWidgets()
|
||||
|
||||
self.maybeAutoSync()
|
||||
|
||||
def _checkForUnclosedWidgets(self) -> None:
|
||||
for w in self.app.topLevelWidgets():
|
||||
if w.isVisible():
|
||||
@ -520,13 +514,16 @@ close the profile or restart Anki."""
|
||||
self.col.reopen()
|
||||
|
||||
def unloadCollection(self, onsuccess: Callable) -> None:
|
||||
def callback():
|
||||
self.setEnabled(False)
|
||||
def after_sync():
|
||||
self.media_syncer.show_diag_until_finished()
|
||||
self._unloadCollection()
|
||||
onsuccess()
|
||||
|
||||
self.closeAllWindows(callback)
|
||||
def before_sync():
|
||||
self.setEnabled(False)
|
||||
self.maybe_auto_sync_on_open_close(after_sync)
|
||||
|
||||
self.closeAllWindows(before_sync)
|
||||
|
||||
def _unloadCollection(self) -> None:
|
||||
if not self.col:
|
||||
@ -869,52 +866,51 @@ title="%s" %s>%s</button>""" % (
|
||||
# Syncing
|
||||
##########################################################################
|
||||
|
||||
# expects a current profile and a loaded collection; reloads
|
||||
# collection after sync completes
|
||||
def onSync(self):
|
||||
def on_sync_button_clicked(self):
|
||||
if self.media_syncer.is_syncing():
|
||||
self.media_syncer.show_sync_log()
|
||||
else:
|
||||
self.temp_sync()
|
||||
# self.unloadCollection(self._onSync)
|
||||
auth = self.pm.sync_auth()
|
||||
if not auth:
|
||||
sync_login(self, self._sync_collection_and_media)
|
||||
else:
|
||||
self._sync_collection_and_media(lambda: None)
|
||||
|
||||
def _onSync(self):
|
||||
self._sync()
|
||||
if not self.loadCollection():
|
||||
return
|
||||
self.media_syncer.start()
|
||||
def _sync_collection_and_media(self, after_sync: Callable[[], None]):
|
||||
"Caller should ensure auth available."
|
||||
# start media sync if not already running
|
||||
if not self.media_syncer.is_syncing():
|
||||
self.media_syncer.start()
|
||||
|
||||
# expects a current profile, but no collection loaded
|
||||
def maybeAutoSync(self) -> None:
|
||||
if (
|
||||
not self.pm.profile["syncKey"]
|
||||
or not self.pm.profile["autoSync"]
|
||||
or self.safeMode
|
||||
or self.restoringBackup
|
||||
):
|
||||
return
|
||||
def on_collection_sync_finished():
|
||||
self.reset()
|
||||
after_sync()
|
||||
|
||||
# ok to sync
|
||||
self._sync()
|
||||
sync_collection(self, on_done=on_collection_sync_finished)
|
||||
|
||||
def maybe_auto_sync_on_open_close(self, after_sync: Callable[[], None]) -> None:
|
||||
"If disabled, after_sync() is called immediately."
|
||||
if self.can_auto_sync():
|
||||
self._sync_collection_and_media(after_sync)
|
||||
else:
|
||||
after_sync()
|
||||
|
||||
def maybe_auto_sync_media(self) -> None:
|
||||
if not self.pm.profile["autoSync"] or self.safeMode or self.restoringBackup:
|
||||
if self.can_auto_sync():
|
||||
return
|
||||
# media_syncer takes care of media syncing preference check
|
||||
self.media_syncer.start()
|
||||
|
||||
def can_auto_sync(self) -> bool:
|
||||
return (self.pm.auto_syncing_enabled()
|
||||
and self.pm.sync_auth()
|
||||
and not self.safeMode
|
||||
and not self.restoringBackup)
|
||||
|
||||
# legacy
|
||||
def _sync(self):
|
||||
from aqt.sync import SyncManager
|
||||
|
||||
self.state = "sync"
|
||||
self.app.setQuitOnLastWindowClosed(False)
|
||||
self.syncer = SyncManager(self, self.pm)
|
||||
self.syncer.sync()
|
||||
self.app.setQuitOnLastWindowClosed(True)
|
||||
|
||||
def temp_sync(self):
|
||||
from aqt.sync import sync
|
||||
|
||||
sync(self)
|
||||
pass
|
||||
onSync = on_sync_button_clicked
|
||||
|
||||
# Tools
|
||||
##########################################################################
|
||||
@ -940,7 +936,7 @@ title="%s" %s>%s</button>""" % (
|
||||
("a", self.onAddCard),
|
||||
("b", self.onBrowse),
|
||||
("t", self.onStats),
|
||||
("y", self.onSync),
|
||||
("y", self.on_sync_button_clicked),
|
||||
]
|
||||
self.applyShortcuts(globalShortcuts)
|
||||
|
||||
|
@ -62,7 +62,7 @@ class MediaSyncer:
|
||||
|
||||
self._log_and_notify(tr(TR.SYNC_MEDIA_STARTING))
|
||||
self._syncing = True
|
||||
self._progress_timer = self.mw.progress.timer(1000, self._on_progress, True)
|
||||
self._progress_timer = self.mw.progress.timer(1000, self._on_progress, False)
|
||||
gui_hooks.media_sync_did_start_or_stop(True)
|
||||
|
||||
def run() -> None:
|
||||
|
@ -618,6 +618,9 @@ create table if not exists profiles
|
||||
def media_syncing_enabled(self) -> bool:
|
||||
return self.profile["syncMedia"]
|
||||
|
||||
def auto_syncing_enabled(self) -> bool:
|
||||
return self.profile["autoSync"]
|
||||
|
||||
def sync_auth(self) -> Optional[SyncAuth]:
|
||||
hkey = self.profile.get("syncKey")
|
||||
if not hkey:
|
||||
|
@ -28,6 +28,10 @@ from aqt.qt import (
|
||||
)
|
||||
from aqt.utils import askUser, askUserDialog, showText, showWarning, tr
|
||||
|
||||
# fixme: catch auth error in other routines, clear sync auth
|
||||
# fixme: sync progress
|
||||
# fixme: curDeck marking collection modified
|
||||
# fixme: show progress immediately
|
||||
|
||||
class FullSyncChoice(enum.Enum):
|
||||
CANCEL = 0
|
||||
@ -40,69 +44,73 @@ def get_sync_status(mw: aqt.main.AnkiQt, callback: Callable[[SyncOutput], None])
|
||||
if not auth:
|
||||
return
|
||||
|
||||
def on_done(fut):
|
||||
def on_future_done(fut):
|
||||
callback(fut.result())
|
||||
|
||||
mw.taskman.run_in_background(lambda: mw.col.backend.sync_status(auth), on_done)
|
||||
mw.taskman.run_in_background(lambda: mw.col.backend.sync_status(auth), on_future_done)
|
||||
|
||||
|
||||
def sync(mw: aqt.main.AnkiQt) -> None:
|
||||
def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
|
||||
auth = mw.pm.sync_auth()
|
||||
if not auth:
|
||||
login(mw, on_success=lambda: sync(mw))
|
||||
sync_login(mw, on_success=lambda: sync_collection(mw))
|
||||
return
|
||||
|
||||
def on_done(fut):
|
||||
def on_future_done(fut):
|
||||
mw.col.db.begin()
|
||||
try:
|
||||
out: SyncOutput = fut.result()
|
||||
except InterruptedError:
|
||||
return
|
||||
return on_done()
|
||||
except Exception as e:
|
||||
showWarning(str(e))
|
||||
return
|
||||
return on_done()
|
||||
|
||||
mw.pm.set_host_number(out.host_number)
|
||||
if out.server_message:
|
||||
showText(out.server_message)
|
||||
if out.required == out.NO_CHANGES:
|
||||
# all done
|
||||
return
|
||||
return on_done()
|
||||
else:
|
||||
full_sync(mw, out)
|
||||
full_sync(mw, out, on_done)
|
||||
|
||||
if not mw.col.basicCheck():
|
||||
showWarning("Please use Tools>Check Database")
|
||||
return
|
||||
return on_done()
|
||||
|
||||
mw.col.save(trx=False)
|
||||
mw.taskman.with_progress(
|
||||
lambda: mw.col.backend.sync_collection(auth),
|
||||
on_done,
|
||||
on_future_done,
|
||||
label=tr(TR.SYNC_CHECKING),
|
||||
)
|
||||
|
||||
|
||||
def full_sync(mw: aqt.main.AnkiQt, out: SyncOutput) -> None:
|
||||
def full_sync(
|
||||
mw: aqt.main.AnkiQt, out: SyncOutput, on_done: Callable[[], None]
|
||||
) -> None:
|
||||
if out.required == out.FULL_DOWNLOAD:
|
||||
confirm_full_download(mw)
|
||||
confirm_full_download(mw, on_done)
|
||||
elif out.required == out.FULL_UPLOAD:
|
||||
full_upload(mw)
|
||||
full_upload(mw, on_done)
|
||||
else:
|
||||
choice = ask_user_to_decide_direction()
|
||||
if choice == FullSyncChoice.UPLOAD:
|
||||
full_upload(mw)
|
||||
full_upload(mw, on_done)
|
||||
elif choice == FullSyncChoice.DOWNLOAD:
|
||||
full_download(mw)
|
||||
full_download(mw, on_done)
|
||||
else:
|
||||
on_done()
|
||||
|
||||
|
||||
def confirm_full_download(mw: aqt.main.AnkiQt) -> None:
|
||||
def confirm_full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
|
||||
# confirmation step required, as some users customize their notetypes
|
||||
# in an empty collection, then want to upload them
|
||||
if not askUser(tr(TR.SYNC_CONFIRM_EMPTY_DOWNLOAD)):
|
||||
return
|
||||
return on_done()
|
||||
else:
|
||||
mw.closeAllWindows(lambda: full_download(mw))
|
||||
mw.closeAllWindows(lambda: full_download(mw, on_done))
|
||||
|
||||
|
||||
def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None:
|
||||
@ -119,7 +127,7 @@ def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None:
|
||||
mw.col.backend.abort_sync()
|
||||
|
||||
|
||||
def full_download(mw: aqt.main.AnkiQt) -> None:
|
||||
def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
|
||||
mw.col.close_for_full_sync()
|
||||
|
||||
def on_timer():
|
||||
@ -129,7 +137,7 @@ def full_download(mw: aqt.main.AnkiQt) -> None:
|
||||
qconnect(timer.timeout, on_timer)
|
||||
timer.start(150)
|
||||
|
||||
def on_done(fut):
|
||||
def on_future_done(fut):
|
||||
timer.stop()
|
||||
mw.col.reopen(after_full_sync=True)
|
||||
mw.reset()
|
||||
@ -137,16 +145,16 @@ def full_download(mw: aqt.main.AnkiQt) -> None:
|
||||
fut.result()
|
||||
except Exception as e:
|
||||
showWarning(str(e))
|
||||
return
|
||||
return on_done()
|
||||
|
||||
mw.taskman.with_progress(
|
||||
lambda: mw.col.backend.full_download(mw.pm.sync_auth()),
|
||||
on_done,
|
||||
on_future_done,
|
||||
label=tr(TR.SYNC_DOWNLOADING_FROM_ANKIWEB),
|
||||
)
|
||||
|
||||
|
||||
def full_upload(mw: aqt.main.AnkiQt) -> None:
|
||||
def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
|
||||
mw.col.close_for_full_sync()
|
||||
|
||||
def on_timer():
|
||||
@ -156,7 +164,7 @@ def full_upload(mw: aqt.main.AnkiQt) -> None:
|
||||
qconnect(timer.timeout, on_timer)
|
||||
timer.start(150)
|
||||
|
||||
def on_done(fut):
|
||||
def on_future_done(fut):
|
||||
timer.stop()
|
||||
mw.col.reopen(after_full_sync=True)
|
||||
mw.reset()
|
||||
@ -164,16 +172,15 @@ def full_upload(mw: aqt.main.AnkiQt) -> None:
|
||||
fut.result()
|
||||
except Exception as e:
|
||||
showWarning(str(e))
|
||||
return
|
||||
return on_done()
|
||||
|
||||
mw.taskman.with_progress(
|
||||
lambda: mw.col.backend.full_upload(mw.pm.sync_auth()),
|
||||
on_done,
|
||||
on_future_done,
|
||||
label=tr(TR.SYNC_UPLOADING_TO_ANKIWEB),
|
||||
)
|
||||
|
||||
|
||||
def login(
|
||||
def sync_login(
|
||||
mw: aqt.main.AnkiQt, on_success: Callable[[], None], username="", password=""
|
||||
) -> None:
|
||||
while True:
|
||||
@ -183,13 +190,13 @@ def login(
|
||||
if username and password:
|
||||
break
|
||||
|
||||
def on_done(fut):
|
||||
def on_future_done(fut):
|
||||
try:
|
||||
auth = fut.result()
|
||||
except SyncError as e:
|
||||
if e.kind() == SyncErrorKind.AUTH_FAILED:
|
||||
showWarning(str(e))
|
||||
login(mw, on_success, username, password)
|
||||
sync_login(mw, on_success, username, password)
|
||||
return
|
||||
except Exception as e:
|
||||
showWarning(str(e))
|
||||
@ -202,7 +209,8 @@ def login(
|
||||
on_success()
|
||||
|
||||
mw.taskman.with_progress(
|
||||
lambda: mw.col.backend.sync_login(username=username, password=password), on_done
|
||||
lambda: mw.col.backend.sync_login(username=username, password=password),
|
||||
on_future_done,
|
||||
)
|
||||
|
||||
|
||||
|
@ -175,7 +175,7 @@ class Toolbar:
|
||||
self.mw.onStats()
|
||||
|
||||
def _syncLinkHandler(self) -> None:
|
||||
self.mw.onSync()
|
||||
self.mw.on_sync_button_clicked()
|
||||
|
||||
# HTML & CSS
|
||||
######################################################################
|
||||
|
Loading…
Reference in New Issue
Block a user