fix(images): background jobs skip files hidden by the removal mark; walkers skip trash folders inside output roots (#501)

- Thumbnail jobs and the legacy scan_output_dir listing leave
  library-removed files out (one mark check, mark_hides).
- Missing-file links also forgets removal marks that hide nothing any
  more (file gone from a folder that exists, or changed), only under a
  readable, non-empty source, re-checked at apply.
- The sidebar Images, Favorites and Needs review counts and the
  collection counts are read again when an image_delete job ends.
- utils/media_folders.py: one skip for thumbnails and trash folders
  (.Trash, .Trash-<uid>, .Trashes, $RECYCLE.BIN) used by every walker,
  the image monitor and the delete review.
This commit is contained in:
Vito Sansevero
2026-10-29 13:25:23 -07:00
parent 66ca988d2a
commit 245d0cdc6a
15 changed files with 635 additions and 91 deletions
+6 -4
View File
@@ -45,6 +45,7 @@ from .foreign_runs import (
store_foreign,
)
from .hashing import generate_prompt_hash
from .media_folders import in_skipped_folder
from .metadata_extractor import ComfyUIMetadataExtractor
from .parallel import note_refusal, refused_at_shutdown
from .logging_config import get_logger
@@ -428,11 +429,12 @@ class ImageGenerationHandler(FileSystemEventHandler):
Returns:
True if the file has a supported image extension and is not
inside a thumbnails directory, False otherwise
inside a thumbnails or trash folder, False otherwise
"""
# Skip files in thumbnails directory - those are derivatives, not
# generated images
if "/thumbnails/" in filepath or "\\thumbnails\\" in filepath:
# Previews are derivatives, and a trash folder inside an output
# folder holds thrown-away files (#501): neither is a new output.
folders = filepath.replace("\\", "/").split("/")[:-1]
if in_skipped_folder(folders):
return False
return filepath.lower().endswith(self.supported_extensions)
+31
View File
@@ -0,0 +1,31 @@
"""The folders no media walker lists below an output root (#501).
``thumbnails`` holds PromptManager's own previews. A trash folder that sits
inside an output root (``D:\\$RECYCLE.BIN`` when ``D:\\`` is a root,
``.Trash-1000`` at a mount point, ``.Trashes`` on a macOS volume) holds files
the user threw away: they are not outputs, so they are never listed, scanned,
linked, tagged or previewed. Every walker asks these predicates instead of
testing names itself; only folders *below* a root are tested.
"""
TRASH_FOLDER_NAMES = frozenset({".trash", ".trashes", "$recycle.bin"})
PREVIEW_FOLDER = "thumbnails"
def is_trash_folder(name: str) -> bool:
"""A trash folder name, in any case: ``.Trash``, ``.Trash-<uid>``,
``.Trashes`` or ``$RECYCLE.BIN``."""
folded = name.casefold()
if folded in TRASH_FOLDER_NAMES:
return True
return folded.startswith(".trash-") and folded[7:].isdigit()
def is_skipped_folder(name: str) -> bool:
"""A folder no walker descends: ``thumbnails`` (exact) or a trash folder."""
return name == PREVIEW_FOLDER or is_trash_folder(name)
def in_skipped_folder(parts) -> bool:
"""Whether any of the folder names *parts* is skipped."""
return any(is_skipped_folder(part) for part in parts)