Store accumulator metadata per entry
This commit is contained in:
+61
-16
@@ -190,16 +190,32 @@ def _entry_value_summary(value: Any) -> str:
|
||||
return text[:160]
|
||||
|
||||
|
||||
def _metadata_copy(value: Any) -> Any:
|
||||
try:
|
||||
return json.loads(json.dumps(value))
|
||||
except Exception:
|
||||
return value
|
||||
|
||||
|
||||
def _entry_metadata(prompt: Any, extra_pnginfo: Any) -> dict[str, Any]:
|
||||
return {
|
||||
"prompt": _metadata_copy(prompt),
|
||||
"extra_pnginfo": _metadata_copy(extra_pnginfo) if isinstance(extra_pnginfo, dict) else {},
|
||||
}
|
||||
|
||||
|
||||
def _entry_infos(store: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
entries = []
|
||||
for index, entry in enumerate(store, start=1):
|
||||
image = entry.get("image")
|
||||
shape = _image_shape(image)
|
||||
has_metadata = "prompt" in entry or "extra_pnginfo" in entry
|
||||
entries.append(
|
||||
{
|
||||
"index": index,
|
||||
"id": str(entry.get("id") or ""),
|
||||
"has_image": image is not None,
|
||||
"has_metadata": has_metadata,
|
||||
"shape": list(shape) if shape is not None else [],
|
||||
"value": _entry_value_summary(entry.get("value")),
|
||||
}
|
||||
@@ -274,12 +290,18 @@ def _metadata(prompt: Any, extra_pnginfo: Any) -> Any:
|
||||
metadata = PngInfo()
|
||||
if prompt is not None:
|
||||
metadata.add_text("prompt", json.dumps(prompt))
|
||||
if extra_pnginfo is not None:
|
||||
if isinstance(extra_pnginfo, dict):
|
||||
for key, value in extra_pnginfo.items():
|
||||
metadata.add_text(key, json.dumps(value))
|
||||
return metadata
|
||||
|
||||
|
||||
def _metadata_for_entry(entry: dict[str, Any], fallback_prompt: Any, fallback_extra_pnginfo: Any) -> Any:
|
||||
prompt = entry["prompt"] if "prompt" in entry else fallback_prompt
|
||||
extra_pnginfo = entry["extra_pnginfo"] if "extra_pnginfo" in entry else fallback_extra_pnginfo
|
||||
return _metadata(prompt, extra_pnginfo)
|
||||
|
||||
|
||||
def _image_to_pil(image: Any) -> Any:
|
||||
_require_image_saving()
|
||||
tensor = image
|
||||
@@ -312,19 +334,26 @@ def _next_save_counter(folder: str, prefix: str) -> int:
|
||||
return counter
|
||||
|
||||
|
||||
def _preview_image_results(images: list[Any], preview_limit: int, prompt: Any, extra_pnginfo: Any) -> list[dict[str, str]]:
|
||||
if not images:
|
||||
def _preview_image_results(
|
||||
entries: list[dict[str, Any]],
|
||||
preview_limit: int,
|
||||
prompt: Any,
|
||||
extra_pnginfo: Any,
|
||||
) -> list[dict[str, str]]:
|
||||
image_entries = [entry for entry in entries if entry.get("image") is not None]
|
||||
if not image_entries:
|
||||
return []
|
||||
_require_image_saving()
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
preview_images = images[: max(1, int(preview_limit))]
|
||||
first_shape = _image_shape(preview_images[0])
|
||||
preview_entries = image_entries[: max(1, int(preview_limit))]
|
||||
first_shape = _image_shape(preview_entries[0].get("image"))
|
||||
height, width = (first_shape[0], first_shape[1]) if first_shape else (512, 512)
|
||||
prefix = "SxCPAccumulatorPreview_temp_" + "".join(random.choice("abcdefghijklmnopqrstupvxyz") for _ in range(5))
|
||||
full_output_folder, filename, counter, subfolder, _filename_prefix = folder_paths.get_save_image_path(prefix, output_dir, width, height)
|
||||
metadata = _metadata(prompt, extra_pnginfo)
|
||||
results = []
|
||||
for image in preview_images:
|
||||
for entry in preview_entries:
|
||||
image = entry.get("image")
|
||||
metadata = _metadata_for_entry(entry, prompt, extra_pnginfo)
|
||||
file = f"{filename}_{counter:05}_.png"
|
||||
_image_to_pil(image).save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=1)
|
||||
results.append({"filename": file, "subfolder": subfolder, "type": "temp"})
|
||||
@@ -343,21 +372,23 @@ def _resolve_save_folder(save_path: str) -> str:
|
||||
|
||||
|
||||
def _save_images_to_folder(
|
||||
images: list[Any],
|
||||
entries: list[dict[str, Any]],
|
||||
save_path: str,
|
||||
filename_prefix: str,
|
||||
prompt: Any,
|
||||
extra_pnginfo: Any,
|
||||
) -> list[str]:
|
||||
if not images:
|
||||
image_entries = [entry for entry in entries if entry.get("image") is not None]
|
||||
if not image_entries:
|
||||
return []
|
||||
folder = _resolve_save_folder(save_path)
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
prefix = _safe_filename_prefix(filename_prefix)
|
||||
counter = _next_save_counter(folder, prefix)
|
||||
metadata = _metadata(prompt, extra_pnginfo)
|
||||
saved_paths = []
|
||||
for image in images:
|
||||
for entry in image_entries:
|
||||
image = entry.get("image")
|
||||
metadata = _metadata_for_entry(entry, prompt, extra_pnginfo)
|
||||
file = f"{prefix}_{counter:05}_.png"
|
||||
path = os.path.join(folder, file)
|
||||
_image_to_pil(image).save(path, pnginfo=metadata, compress_level=4)
|
||||
@@ -642,6 +673,8 @@ class SxCPAccumulator:
|
||||
"entry_tag": ("STRING", {"default": "", "multiline": False}),
|
||||
},
|
||||
"hidden": {
|
||||
"prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
"unique_id": "UNIQUE_ID",
|
||||
},
|
||||
}
|
||||
@@ -681,18 +714,27 @@ class SxCPAccumulator:
|
||||
return value[image_index]
|
||||
return value
|
||||
|
||||
def _entry_records(self, image: Any, value: Any, entry_id: Any, entry_tag: str, skip_empty: bool) -> list[dict[str, Any]]:
|
||||
def _entry_records(
|
||||
self,
|
||||
image: Any,
|
||||
value: Any,
|
||||
entry_id: Any,
|
||||
entry_tag: str,
|
||||
skip_empty: bool,
|
||||
metadata: dict[str, Any],
|
||||
) -> list[dict[str, Any]]:
|
||||
images = _split_image_value(image)
|
||||
if not images:
|
||||
if value is None and skip_empty:
|
||||
return []
|
||||
return [{"id": self._entry_id(entry_id, entry_tag, 0, 1), "image": None, "value": value}]
|
||||
return [{"id": self._entry_id(entry_id, entry_tag, 0, 1), "image": None, "value": value, **metadata}]
|
||||
image_count = len(images)
|
||||
return [
|
||||
{
|
||||
"id": self._entry_id(entry_id, entry_tag, index, image_count),
|
||||
"image": image_item,
|
||||
"value": self._value_for_image(value, index, image_count),
|
||||
**metadata,
|
||||
}
|
||||
for index, image_item in enumerate(images)
|
||||
]
|
||||
@@ -748,6 +790,8 @@ class SxCPAccumulator:
|
||||
value=None,
|
||||
entry_id=None,
|
||||
entry_tag="",
|
||||
prompt=None,
|
||||
extra_pnginfo=None,
|
||||
unique_id=None,
|
||||
):
|
||||
key = self._store_key(store_key, unique_id)
|
||||
@@ -758,7 +802,8 @@ class SxCPAccumulator:
|
||||
store.clear()
|
||||
|
||||
if action not in ("clear", "read"):
|
||||
entries = self._entry_records(image, value, entry_id, entry_tag, bool(skip_empty))
|
||||
metadata = _entry_metadata(prompt, extra_pnginfo)
|
||||
entries = self._entry_records(image, value, entry_id, entry_tag, bool(skip_empty), metadata)
|
||||
self._append_or_replace(store, entries, action)
|
||||
|
||||
max_items = max(1, int(max_items))
|
||||
@@ -855,14 +900,14 @@ class SxCPAccumulatorPreview:
|
||||
saved_paths: list[str] = []
|
||||
save_status = ""
|
||||
if bool(save_batch) and bool(finished):
|
||||
saved_paths = _save_images_to_folder(images, save_path, filename_prefix, prompt, extra_pnginfo)
|
||||
saved_paths = _save_images_to_folder(store, save_path, filename_prefix, prompt, extra_pnginfo)
|
||||
save_status = f"; saved={len(saved_paths)}"
|
||||
if saved_paths and bool(clear_after_save):
|
||||
store.clear()
|
||||
images = []
|
||||
save_status += "; cleared_after_save"
|
||||
|
||||
preview_images = _preview_image_results(images, preview_limit, prompt, extra_pnginfo)
|
||||
preview_images = _preview_image_results(store, preview_limit, prompt, extra_pnginfo)
|
||||
entries = _entry_infos(store)
|
||||
status = _accumulator_status(key, store)
|
||||
if removed:
|
||||
|
||||
@@ -64,7 +64,8 @@ function entryLabel(entry) {
|
||||
const id = entry?.id ? ` ${entry.id}` : "";
|
||||
const image = entry?.has_image ? " image" : " value";
|
||||
const shape = Array.isArray(entry?.shape) && entry.shape.length >= 2 ? ` ${entry.shape[1]}x${entry.shape[0]}` : "";
|
||||
return `#${index}${id}${image}${shape}`.trim();
|
||||
const metadata = entry?.has_metadata ? " metadata" : "";
|
||||
return `#${index}${id}${image}${shape}${metadata}`.trim();
|
||||
}
|
||||
|
||||
function setStatus(node, status) {
|
||||
|
||||
Reference in New Issue
Block a user