Batch download: incremental saved-dot updates + browse-only gating
Two related fixes for the File → Batch Download Page (Ctrl+D) flow.
1. Saved-dot refresh
Pre-fix: when the user picked a destination inside the library, the
batch wrote files to disk but the browse grid's saved-dots stayed dark
until the next refresh. The grid was lying about local state.
Fix: stash the chosen destination as self._batch_dest at the dispatch
site, then in _on_batch_progress (which already fires per-file via
the existing batch_progress signal) check whether dest is inside
saved_dir(); if so, find the just-finished post in self._posts by id
and light its grid thumb's saved-locally dot. Dots appear incrementally
as each file lands, not all at once at the end.
The batch_progress signal grew a third int param (post_id of the
just-finished item). It's a single-consumer signal — only
_on_batch_progress connects to it — so the shape change is local.
Both batch download paths (the file menu's _batch_download and the
multi-select menu's _batch_download_posts) pass post.id through.
When the destination is OUTSIDE the library, dots stay dark — the
saved-dot means "in library", not "downloaded somewhere". The check
uses Path.is_relative_to (Python 3.11+).
self._batch_dest is cleared in _on_batch_done after the batch finishes
so a subsequent non-batch save doesn't accidentally see a stale dest.
2. Tab gating
Pre-fix: File → Batch Download Page... was enabled on Bookmarks and
Library tabs, where it makes no sense (those tabs already show local
files). Ctrl+D fired regardless of active tab.
Fix: store the QAction as self._batch_action instead of a local var
in _setup_menu, then toggle setEnabled(index == 0) from _switch_view.
Disabling the QAction also disables its keyboard shortcut, so Ctrl+D
becomes a no-op on non-browse tabs without a separate guard.
Verified manually:
- Browse tab → menu enabled, Ctrl+D works
- Bookmarks/Library tabs → menu grayed out, Ctrl+D no-op
- Batch dl into ~/.local/share/booru-viewer/saved → dots light up
one-by-one as files land
- Batch dl into /tmp → files written, dots stay dark
This commit is contained in:
parent
dbc530bb3c
commit
9455ff0f03
@ -23,7 +23,7 @@ class AsyncSignals(QObject):
|
|||||||
bookmark_done = Signal(int, str)
|
bookmark_done = Signal(int, str)
|
||||||
bookmark_error = Signal(str)
|
bookmark_error = Signal(str)
|
||||||
autocomplete_done = Signal(list)
|
autocomplete_done = Signal(list)
|
||||||
batch_progress = Signal(int, int) # current, total
|
batch_progress = Signal(int, int, int) # current, total, post_id (of the just-finished item)
|
||||||
batch_done = Signal(str)
|
batch_done = Signal(str)
|
||||||
download_progress = Signal(int, int) # bytes_downloaded, total_bytes
|
download_progress = Signal(int, int) # bytes_downloaded, total_bytes
|
||||||
prefetch_progress = Signal(int, float) # index, progress (0-1 or -1 to hide)
|
prefetch_progress = Signal(int, float) # index, progress (0-1 or -1 to hide)
|
||||||
|
|||||||
@ -465,10 +465,10 @@ class BooruApp(QMainWindow):
|
|||||||
|
|
||||||
file_menu.addSeparator()
|
file_menu.addSeparator()
|
||||||
|
|
||||||
batch_action = QAction("Batch &Download Page...", self)
|
self._batch_action = QAction("Batch &Download Page...", self)
|
||||||
batch_action.setShortcut(QKeySequence("Ctrl+D"))
|
self._batch_action.setShortcut(QKeySequence("Ctrl+D"))
|
||||||
batch_action.triggered.connect(self._batch_download)
|
self._batch_action.triggered.connect(self._batch_download)
|
||||||
file_menu.addAction(batch_action)
|
file_menu.addAction(self._batch_action)
|
||||||
|
|
||||||
file_menu.addSeparator()
|
file_menu.addSeparator()
|
||||||
|
|
||||||
@ -538,6 +538,11 @@ class BooruApp(QMainWindow):
|
|||||||
self._browse_btn.setChecked(index == 0)
|
self._browse_btn.setChecked(index == 0)
|
||||||
self._bookmark_btn.setChecked(index == 1)
|
self._bookmark_btn.setChecked(index == 1)
|
||||||
self._library_btn.setChecked(index == 2)
|
self._library_btn.setChecked(index == 2)
|
||||||
|
# Batch Download (Ctrl+D / File menu) only makes sense on browse —
|
||||||
|
# bookmarks and library tabs already show local files, downloading
|
||||||
|
# them again is meaningless. Disabling the QAction also disables
|
||||||
|
# its keyboard shortcut.
|
||||||
|
self._batch_action.setEnabled(index == 0)
|
||||||
# Clear grid selections and current post to prevent cross-tab action conflicts
|
# Clear grid selections and current post to prevent cross-tab action conflicts
|
||||||
# Preview media stays visible but actions are disabled until a new post is selected
|
# Preview media stays visible but actions are disabled until a new post is selected
|
||||||
self._grid.clear_selection()
|
self._grid.clear_selection()
|
||||||
@ -2622,6 +2627,9 @@ class BooruApp(QMainWindow):
|
|||||||
self._run_async(_fav)
|
self._run_async(_fav)
|
||||||
|
|
||||||
def _batch_download_posts(self, posts: list, dest: str) -> None:
|
def _batch_download_posts(self, posts: list, dest: str) -> None:
|
||||||
|
# Same _batch_dest stash as _batch_download — _on_batch_progress
|
||||||
|
# incrementally lights saved dots when dest is inside the library.
|
||||||
|
self._batch_dest = Path(dest)
|
||||||
async def _batch():
|
async def _batch():
|
||||||
for i, post in enumerate(posts):
|
for i, post in enumerate(posts):
|
||||||
try:
|
try:
|
||||||
@ -2631,7 +2639,7 @@ class BooruApp(QMainWindow):
|
|||||||
if not target.exists():
|
if not target.exists():
|
||||||
import shutil
|
import shutil
|
||||||
shutil.copy2(path, target)
|
shutil.copy2(path, target)
|
||||||
self._signals.batch_progress.emit(i + 1, len(posts))
|
self._signals.batch_progress.emit(i + 1, len(posts), post.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Batch #{post.id} failed: {e}")
|
log.warning(f"Batch #{post.id} failed: {e}")
|
||||||
self._signals.batch_done.emit(f"Downloaded {len(posts)} images to {dest}")
|
self._signals.batch_done.emit(f"Downloaded {len(posts)} images to {dest}")
|
||||||
@ -2814,6 +2822,9 @@ class BooruApp(QMainWindow):
|
|||||||
if not dest:
|
if not dest:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Stash dest so _on_batch_done can decide whether the destination
|
||||||
|
# is inside the library and the saved-dots need refreshing.
|
||||||
|
self._batch_dest = Path(dest)
|
||||||
posts = list(self._posts)
|
posts = list(self._posts)
|
||||||
self._status.showMessage(f"Downloading {len(posts)} images...")
|
self._status.showMessage(f"Downloading {len(posts)} images...")
|
||||||
|
|
||||||
@ -2826,15 +2837,29 @@ class BooruApp(QMainWindow):
|
|||||||
if not target.exists():
|
if not target.exists():
|
||||||
import shutil
|
import shutil
|
||||||
shutil.copy2(path, target)
|
shutil.copy2(path, target)
|
||||||
self._signals.batch_progress.emit(i + 1, len(posts))
|
self._signals.batch_progress.emit(i + 1, len(posts), post.id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.warning(f"Batch #{post.id} failed: {e}")
|
log.warning(f"Batch #{post.id} failed: {e}")
|
||||||
self._signals.batch_done.emit(f"Downloaded {len(posts)} images to {dest}")
|
self._signals.batch_done.emit(f"Downloaded {len(posts)} images to {dest}")
|
||||||
|
|
||||||
self._run_async(_batch)
|
self._run_async(_batch)
|
||||||
|
|
||||||
def _on_batch_progress(self, current: int, total: int) -> None:
|
def _on_batch_progress(self, current: int, total: int, post_id: int) -> None:
|
||||||
self._status.showMessage(f"Downloading {current}/{total}...")
|
self._status.showMessage(f"Downloading {current}/{total}...")
|
||||||
|
# Light the browse saved-dot for the just-finished post if the
|
||||||
|
# batch destination is inside the library. Runs per-post on the
|
||||||
|
# main thread (this is a Qt slot), so the dot appears as the
|
||||||
|
# files land instead of all at once when the batch completes.
|
||||||
|
dest = getattr(self, "_batch_dest", None)
|
||||||
|
if dest is None:
|
||||||
|
return
|
||||||
|
from ..core.config import saved_dir
|
||||||
|
if not dest.is_relative_to(saved_dir()):
|
||||||
|
return
|
||||||
|
for i, p in enumerate(self._posts):
|
||||||
|
if p.id == post_id and i < len(self._grid._thumbs):
|
||||||
|
self._grid._thumbs[i].set_saved_locally(True)
|
||||||
|
break
|
||||||
|
|
||||||
# -- Toggles --
|
# -- Toggles --
|
||||||
|
|
||||||
@ -3101,6 +3126,9 @@ class BooruApp(QMainWindow):
|
|||||||
self._bookmarks_view.refresh()
|
self._bookmarks_view.refresh()
|
||||||
if self._stack.currentIndex() == 2:
|
if self._stack.currentIndex() == 2:
|
||||||
self._library_view.refresh()
|
self._library_view.refresh()
|
||||||
|
# Saved-dot updates happen incrementally in _on_batch_progress as
|
||||||
|
# each file lands; this slot just clears the destination stash.
|
||||||
|
self._batch_dest = None
|
||||||
|
|
||||||
def closeEvent(self, event) -> None:
|
def closeEvent(self, event) -> None:
|
||||||
# Flush any pending splitter / window-state saves (debounce timers
|
# Flush any pending splitter / window-state saves (debounce timers
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user