diff --git a/proto/backend.proto b/proto/backend.proto index 04809783c..4d387ec02 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -55,6 +55,7 @@ message BackendInput { Empty all_deck_config = 43; Empty new_deck_config = 44; int64 remove_deck_config = 45; + Empty abort_media_sync = 46; } } @@ -70,6 +71,7 @@ message BackendOutput { string format_time_span = 31; string studied_today = 32; string congrats_learn_msg = 33; + Empty abort_media_sync = 46; // fallible commands TemplateRequirementsOut template_requirements = 16; diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 886e3dc9d..8d39ff70c 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -519,6 +519,9 @@ class RustBackend: def remove_deck_config(self, dcid: int) -> None: self._run_command(pb.BackendInput(remove_deck_config=dcid)) + def abort_media_sync(self): + self._run_command(pb.BackendInput(abort_media_sync=pb.Empty())) + def translate_string_in( key: TR, **kwargs: Union[str, int, float] diff --git a/qt/aqt/mediasync.py b/qt/aqt/mediasync.py index ebafee8d6..44fc8b3e7 100644 --- a/qt/aqt/mediasync.py +++ b/qt/aqt/mediasync.py @@ -123,6 +123,7 @@ class MediaSyncer: return self._log_and_notify(tr(TR.SYNC_MEDIA_ABORTING)) self._want_stop = True + self.mw.col.backend.abort_media_sync() def is_syncing(self) -> bool: return self._syncing diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 906fb1692..099a21e0c 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -39,6 +39,7 @@ slog-envlogger = "2.2.0" serde_repr = "0.1.5" num_enum = "0.4.2" unicase = "2.6.0" +futures = "0.3.4" # pinned until rusqlite 0.22 comes out [target.'cfg(target_vendor="apple")'.dependencies.rusqlite] diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index d8c70769a..4cf7f09ee 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -30,6 +30,7 @@ use crate::timestamp::TimestampSecs; use crate::types::Usn; use crate::{backend_proto as pb, log}; use fluent::FluentValue; +use futures::future::{AbortHandle, Abortable}; use log::error; use prost::Message; use std::collections::{HashMap, HashSet}; @@ -47,6 +48,7 @@ pub struct Backend { progress_callback: Option, i18n: I18n, server: bool, + media_sync_abort: Option, } enum Progress<'a> { @@ -135,6 +137,7 @@ impl Backend { progress_callback: None, i18n, server, + media_sync_abort: None, } } @@ -274,6 +277,10 @@ impl Backend { self.remove_deck_config(dcid)?; OValue::RemoveDeckConfig(pb::Empty {}) } + Value::AbortMediaSync(_) => { + self.abort_media_sync(); + OValue::AbortMediaSync(pb::Empty {}) + } }) } @@ -481,9 +488,7 @@ impl Backend { }) } - // fixme: will block other db access - - fn sync_media(&self, input: SyncMediaIn) -> Result<()> { + fn sync_media(&mut self, input: SyncMediaIn) -> Result<()> { let mut guard = self.col.lock().unwrap(); let col = guard.as_mut().unwrap(); @@ -503,19 +508,38 @@ impl Backend { } fn sync_media_inner( - &self, + &mut self, input: pb::SyncMediaIn, folder: PathBuf, db: PathBuf, log: Logger, ) -> Result<()> { + let (abort_handle, abort_reg) = AbortHandle::new_pair(); + self.media_sync_abort = Some(abort_handle); + let callback = |progress: &MediaSyncProgress| { self.fire_progress_callback(Progress::MediaSync(progress)) }; let mgr = MediaManager::new(&folder, &db)?; let mut rt = Runtime::new().unwrap(); - rt.block_on(mgr.sync_media(callback, &input.endpoint, &input.hkey, log)) + let sync_fut = mgr.sync_media(callback, &input.endpoint, &input.hkey, log); + let abortable_sync = Abortable::new(sync_fut, abort_reg); + let ret = match rt.block_on(abortable_sync) { + Ok(sync_result) => sync_result, + Err(_) => { + // aborted sync + Ok(()) + } + }; + self.media_sync_abort = None; + ret + } + + fn abort_media_sync(&mut self) { + if let Some(handle) = self.media_sync_abort.take() { + handle.abort(); + } } fn check_media(&self) -> Result {