diff --git a/app/assets/services/__init__.py b/app/assets/services/__init__.py index 11fcb4122..77fc8d182 100644 --- a/app/assets/services/__init__.py +++ b/app/assets/services/__init__.py @@ -23,6 +23,7 @@ from app.assets.services.ingest import ( DependencyMissingError, HashMismatchError, create_from_hash, + ingest_existing_file, upload_from_temp_path, ) from app.assets.database.queries import ( @@ -72,6 +73,7 @@ __all__ = [ "delete_asset_reference", "get_asset_by_hash", "get_asset_detail", + "ingest_existing_file", "get_mtime_ns", "get_size_and_mtime_ns", "list_assets_page", diff --git a/app/assets/services/ingest.py b/app/assets/services/ingest.py index 44d7aef36..86e6ac521 100644 --- a/app/assets/services/ingest.py +++ b/app/assets/services/ingest.py @@ -26,6 +26,7 @@ from app.assets.helpers import normalize_tags from app.assets.services.file_utils import get_size_and_mtime_ns from app.assets.services.path_utils import ( compute_relative_filename, + get_name_and_tags_from_asset_path, resolve_destination_from_tags, validate_path_within_base, ) @@ -128,6 +129,42 @@ def _ingest_file_from_path( ) +def ingest_existing_file( + abs_path: str, + user_metadata: UserMetadata = None, + extra_tags: Sequence[str] = (), + tag_origin: str = "automatic", + owner_id: str = "", +) -> IngestResult: + """Register an existing on-disk file as an asset. + + Handles stat, BLAKE3 hash, MIME detection, and path-based tag derivation. + Deduplicates by hash (same content → same Asset, new AssetReference). + """ + size_bytes, mtime_ns = get_size_and_mtime_ns(abs_path) + + digest, _ = hashing.compute_blake3_hash(abs_path) + asset_hash = "blake3:" + digest + + mime_type = mimetypes.guess_type(abs_path, strict=False)[0] + + name, path_tags = get_name_and_tags_from_asset_path(abs_path) + tags = list(dict.fromkeys(path_tags + list(extra_tags))) + + return _ingest_file_from_path( + abs_path=abs_path, + asset_hash=asset_hash, + size_bytes=size_bytes, + mtime_ns=mtime_ns, + mime_type=mime_type, + info_name=name, + user_metadata=user_metadata, + tags=tags, + tag_origin=tag_origin, + owner_id=owner_id, + ) + + def _register_existing_asset( asset_hash: str, name: str, diff --git a/main.py b/main.py index 2aba54e14..5b5f15eb3 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ import time from comfy.cli_args import args, enables_dynamic_vram from app.logger import setup_logger from app.assets.seeder import asset_seeder +from app.assets.services import ingest_existing_file import itertools import utils.extra_config from utils.mime_types import init_mime_types @@ -229,6 +230,44 @@ def cuda_malloc_warning(): logging.warning("\nWARNING: this card most likely does not support cuda-malloc, if you get \"CUDA error\" please run ComfyUI with: --disable-cuda-malloc\n") +def _register_execution_outputs(history_result: dict, prompt_id: str) -> int: + """Register output files from a completed execution as assets.""" + outputs = history_result.get("outputs", {}) + if not outputs: + return 0 + + registered = 0 + for node_id, node_output in outputs.items(): + for key, items in node_output.items(): + if not isinstance(items, list): + continue + for item in items: + if not isinstance(item, dict): + continue + if item.get("type") != "output": + continue + filename = item.get("filename") + subfolder = item.get("subfolder", "") + if not filename: + continue + + base_dir = folder_paths.get_directory_by_type("output") + abs_path = os.path.join(base_dir, subfolder, filename) + if not os.path.isfile(abs_path): + continue + + try: + ingest_existing_file( + abs_path, + user_metadata={"prompt_id": prompt_id}, + ) + registered += 1 + except Exception: + logging.exception("Failed to register output: %s", abs_path) + + return registered + + def prompt_worker(q, server_instance): current_time: float = 0.0 cache_type = execution.CacheType.CLASSIC @@ -264,6 +303,8 @@ def prompt_worker(q, server_instance): was_paused = asset_seeder.pause() try: e.execute(item[2], prompt_id, extra_data, item[4]) + if not asset_seeder.is_disabled(): + _register_execution_outputs(e.history_result, prompt_id) finally: if was_paused: asset_seeder.resume()