From b72f3a54c02803f4e1815a0ad41987bb125df23f Mon Sep 17 00:00:00 2001 From: pax Date: Thu, 9 Apr 2026 17:03:37 -0500 Subject: [PATCH] main_window: route _bulk_save through save_post_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Second Phase 2 site migration. Hoists destination resolution out of the per-iteration loop, uses a shared in_flight set so collision-prone templates (%artist% on a page of same-artist posts) get sequential suffixes instead of clobbering each other, and finally calls _copy_library_thumb so multi-select bulk saves get library thumbnails just like single-post saves do. Drops the dead site_id assignment that nothing read. Fixes the latent bug where _bulk_save left library thumbnails uncopied even though _save_to_library always copied them — multi-select saves were missing thumbnails in the Library tab until you re-saved one at a time. --- booru_viewer/gui/main_window.py | 38 ++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/booru_viewer/gui/main_window.py b/booru_viewer/gui/main_window.py index bab1634..770609e 100644 --- a/booru_viewer/gui/main_window.py +++ b/booru_viewer/gui/main_window.py @@ -2589,31 +2589,35 @@ class BooruApp(QMainWindow): self._run_async(_do) def _bulk_save(self, indices: list[int], posts: list[Post], folder: str | None) -> None: - site_id = self._site_combo.currentData() + """Bulk-save the selected posts into the library, optionally inside a subfolder. + + Each iteration routes through save_post_file with a shared + in_flight set so template-collision-prone batches (e.g. + %artist% on a page that has many posts by the same artist) get + sequential _1, _2, _3 suffixes instead of clobbering each other. + """ + from ..core.config import saved_dir, saved_folder_dir + from ..core.library_save import save_post_file + where = folder or "Unfiled" self._status.showMessage(f"Saving {len(posts)} to {where}...") + try: + dest_dir = saved_folder_dir(folder) if folder else saved_dir() + except ValueError as e: + self._status.showMessage(f"Invalid folder name: {e}") + return + + in_flight: set[str] = set() async def _do(): - from ..core.config import saved_dir, saved_folder_dir - import shutil for i, (idx, post) in enumerate(zip(indices, posts)): try: - path = await download_image(post.file_url) - ext = Path(path).suffix - dest_dir = saved_folder_dir(folder) if folder else saved_dir() - dest = dest_dir / f"{post.id}{ext}" - if not dest.exists(): - shutil.copy2(path, dest) - # Store metadata for library search - self._db.save_library_meta( - post_id=post.id, tags=post.tags, - tag_categories=post.tag_categories, - score=post.score, rating=post.rating, - source=post.source, file_url=post.file_url, - ) + src = Path(await download_image(post.file_url)) + save_post_file(src, post, dest_dir, self._db, in_flight) + self._copy_library_thumb(post) self._signals.bookmark_done.emit(idx, f"Saved {i+1}/{len(posts)} to {where}") except Exception as e: - log.warning(f"Operation failed: {e}") + log.warning(f"Bulk save #{post.id} failed: {e}") self._signals.batch_done.emit(f"Saved {len(posts)} to {where}") self._run_async(_do)