diff --git a/proto/backend.proto b/proto/backend.proto index ac6acd6bc..b4c555e8a 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -42,6 +42,7 @@ message BackendInput { FormatTimeSpanIn format_time_span = 31; StudiedTodayIn studied_today = 32; CongratsLearnMsgIn congrats_learn_msg = 33; + Empty empty_trash = 34; } } @@ -68,6 +69,7 @@ message BackendOutput { Empty sync_media = 27; MediaCheckOut check_media = 28; Empty trash_media_files = 29; + Empty empty_trash = 34; BackendError error = 2047; } @@ -281,6 +283,7 @@ message MediaCheckOut { repeated string unused = 1; repeated string missing = 2; string report = 3; + bool have_trash = 4; } message TrashMediaFilesIn { diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 22f9e547a..247f580c1 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -360,6 +360,8 @@ class RustBackend: ) ).congrats_learn_msg + def empty_trash(self): + self._run_command(pb.BackendInput(empty_trash=pb.Empty())) def translate_string_in( key: TR, **kwargs: Union[str, int, float] diff --git a/qt/aqt/mediacheck.py b/qt/aqt/mediacheck.py index 7500ceafd..61bc491e6 100644 --- a/qt/aqt/mediacheck.py +++ b/qt/aqt/mediacheck.py @@ -95,6 +95,11 @@ class MediaChecker: box.addButton(b, QDialogButtonBox.RejectRole) b.clicked.connect(self._on_render_latex) # type: ignore + if output.have_trash: + b = QPushButton(tr(TR.MEDIA_CHECK_EMPTY_TRASH)) + b.setAutoDefault(False) + box.addButton(b, QDialogButtonBox.RejectRole) + b.clicked.connect(lambda c: self._on_empty_trash()) # type: ignore box.rejected.connect(diag.reject) # type: ignore diag.setMinimumHeight(400) diag.setMinimumWidth(500) @@ -150,3 +155,20 @@ class MediaChecker: self.progress_dialog = None tooltip(tr(TR.MEDIA_CHECK_DELETE_UNUSED_COMPLETE, count=total)) + + def _on_empty_trash(self): + self.progress_dialog = self.mw.progress.start() + hooks.bg_thread_progress_callback.append(self._on_progress) + + def empty_trash(): + self.mw.col.backend.empty_trash() + + def on_done(fut: Future): + self.mw.progress.finish() + hooks.bg_thread_progress_callback.remove(self._on_progress) + # check for errors + fut.result() + + tooltip(tr(TR.MEDIA_CHECK_TRASH_EMPTIED)) + + self.mw.taskman.run_in_background(empty_trash, on_done) diff --git a/rslib/ftl/media-check.ftl b/rslib/ftl/media-check.ftl index b0a324279..739d1f3cc 100644 --- a/rslib/ftl/media-check.ftl +++ b/rslib/ftl/media-check.ftl @@ -47,6 +47,8 @@ media-check-delete-unused-complete = {$count -> *[other] {$count} files } moved to the trash. +media-check-trash-emptied = The trash folder is now empty. + ## Rendering LaTeX media-check-all-latex-rendered = All LaTeX rendered. @@ -55,3 +57,5 @@ media-check-all-latex-rendered = All LaTeX rendered. media-check-delete-unused = Delete Unused media-check-render-latex = Render LaTeX +# button to permanently delete media files from the trash folder +media-check-empty-trash = Empty Trash diff --git a/rslib/src/backend.rs b/rslib/src/backend.rs index 8997ff71d..e074dd960 100644 --- a/rslib/src/backend.rs +++ b/rslib/src/backend.rs @@ -233,6 +233,10 @@ impl Backend { input.next_due, &self.i18n, )), + Value::EmptyTrash(_) => { + self.empty_trash()?; + OValue::EmptyTrash(Empty {}) + } }) } @@ -413,6 +417,7 @@ impl Backend { unused: output.unused, missing: output.missing, report, + have_trash: output.trash_count > 0, }) } @@ -452,6 +457,16 @@ impl Backend { } } } + + fn empty_trash(&self) -> Result<()> { + let callback = + |progress: usize| self.fire_progress_callback(Progress::MediaCheck(progress as u32)); + + let mgr = MediaManager::new(&self.media_folder, &self.media_db)?; + let mut checker = MediaChecker::new(&mgr, &self.col_path, callback, &self.i18n, &self.log); + + checker.empty_trash() + } } fn translate_arg_to_fluent_val(arg: &pb::TranslateArgValue) -> FluentValue {