release 2.44 worky

- final bugfixes and working loop for filename building, tested for all case scenarios
	modified:   README.md
	modified:   commit.gitsync.2.43.txt
	modified:   save_image_extended.py
This commit is contained in:
audioscavenger
2024-05-08 10:14:49 -07:00
parent 4ab2249c02
commit 983f5316f6
3 changed files with 95 additions and 56 deletions
+5 -1
View File
@@ -1,5 +1,5 @@
# Save Image Extended for ComfyUI
version = 2.43 - **AVIF** support!
**AVIF** support!
<p align="center">
<img src="assets/save-image-extended-comfyui-example.png" />
@@ -123,6 +123,10 @@ However, I do provide a way to contact me, and will accept PR and collabs. Once
- [ ] improve get_latest_counter: fails when user renames files: appends text after counter
- [ ] offer to place the counter anywhere, as a key in filename_keys
### release 2.44
- so many bugfixes
- complete rework of generate_custom_name to handle ALL the possible scenarios
### release 2.43
- [x] support for AVIF
- [x] added requirements.txt
+8 -5
View File
@@ -1,8 +1,11 @@
release 2.43 AVIF
- bugfix: rework of generate_custom_name to cover all subfolders scenarios
- [x] support for AVIF
- [x] added requirements.txt
modified: README.md
2.43 release 2.43 AVIF
- bugfix: rework of generate_custom_name to cover all subfolders scenarios
- [x] support for AVIF
- [x] added requirements.txt
modified: README.md
modified: save_image_extended.py
modified: save_image_extended.py
modified: commit.gitsync.2.43.txt
modified: README.md
modified: save_image_extended.py
modified: commit.gitsync.2.43.txt
+82 -50
View File
@@ -26,8 +26,7 @@ original_locale = locale.setlocale(locale.LC_TIME, '')
# class SaveImageExtended -------------------------------------------------------------------------------
class SaveImageExtended:
#Version: 2.43
version = 2.43
version = 2.44
type = 'output'
png_compress_level = 9
@@ -35,10 +34,10 @@ class SaveImageExtended:
webp_quality = 91
jpeg_quality = 91
filename_prefix = 'myFile'
filename_prefix = 'ComfyUI'
filename_keys = 'sampler_name, scheduler, cfg, steps'
foldername_prefix = 'myFolder'
foldername_keys = 'ckpt_name, ./exampleSubfolder'
foldername_prefix = ''
foldername_keys = 'ckpt_name, ./subfolder'
delimiter = '_'
save_job_data = 'basic, models, sampler, prompt'
job_data_per_image = False
@@ -95,7 +94,7 @@ class SaveImageExtended:
'basic, prompt',
'basic, sampler, prompt',
'basic, models, sampler, prompt'
],{'default': self.save_job_data}),
], {'default': self.save_job_data}),
'job_data_per_image': ([False, True], {'default': self.job_data_per_image}),
'job_custom_text': ('STRING', {'default': self.job_custom_text, 'multiline': False}),
'save_metadata': ([True, False], {'default': self.save_metadata}),
@@ -147,9 +146,9 @@ class SaveImageExtended:
if counter_position not in self.counter_positions: counter_position = self.counter_position
if counter_position == 'last':
# BUG: this works only if extension is 3 letters like png, this will break with webp and avif:
counters = [int(file[-(extLen + counter_digits):-extLen]) if file[-(extLen + counter_digits):-extLen].isdigit() else 0 for file in files if one_counter_per_folder or file.startswith(filename_prefix)]
counters = [int(file[-(extLen + counter_digits):-extLen]) if file[-(extLen + counter_digits):-extLen].isdecimal() else 0 for file in files if one_counter_per_folder or file.startswith(filename_prefix)]
else:
counters = [int(file[:counter_digits]) if file[:counter_digits].isdigit() else 0 for file in files if one_counter_per_folder or file[counter_digits +1:].startswith(filename_prefix)]
counters = [int(file[:counter_digits]) if file[:counter_digits].isdecimal() else 0 for file in files if one_counter_per_folder or file[counter_digits +1:].startswith(filename_prefix)]
if counters:
counter = max(counters) + 1
@@ -204,9 +203,15 @@ class SaveImageExtended:
return found_values
def generate_custom_name(self, keys_to_extract, prefix, delimiter, resolution, prompt):
# String Type Example isdecimal() isdigit() isnumeric()
# --------------------------- --------- ----------- --------- -----------
# Base 10 Numbers '0123' True True True
# Fractions and Superscripts '⅔','2²' False True True
# Roman Numerals 'ↁ' False False True
# --------------------------- --------- ----------- --------- -----------
def generate_custom_name(self, keys_to_extract, prefix, delimiter, prompt):
custom_name = prefix
if prompt is not None and len(keys_to_extract) > 0:
if prompt is not None and keys_to_extract != ['']:
found_values = {}
# print(f"debug generate_custom_name: --prefix: {prefix}")
# print(f"debug generate_custom_name: --keys_to_extract: {keys_to_extract}")
@@ -222,41 +227,54 @@ class SaveImageExtended:
# 'denoise': 1.0, ...
for key in keys_to_extract:
value = None
node, nodeKey = None, None
# check if this is a subfolder: starts with ./ or /, can also end with /
if '/' in key:
# key is a fsubfolder
value = key
else:
node, nodeKey = None, None
splitKey = key.split('.')
# we also exclude cases line subfolders: "./folder" - or typos like "smth."
if len(splitKey) > 1 and splitKey[0] is not None and splitKey[1] is not None:
# if you enter a key like 1.sampler, we will look for sampler value in node #1
node, nodeKey = splitKey[0], splitKey[1]
if node in prompt:
# print(f"debug generate_custom_name: --node.nodeKey = {node}.{nodeKey}")
# splitKey[0] = #node number found in prompt, we will recurse only in that node:
self.find_keys_recursively(prompt[node], [nodeKey], found_values)
# else:
# # if splitKey[0] = #node number not found in prompt, we will just inform the user
# print(f"SaveImageExtended info: node #{node} not found")
# we also exclude cases like "string." or ".string" or "string.string"
if len(splitKey) > 1:
# key has the form string.string
if '' not in splitKey:
# key has the form string.string
if splitKey[0].isdecimal():
# key has the form num.widget_name like 123.widget_name, we will then look for widget_name value in node #123
node, nodeKey = splitKey[0], splitKey[1]
if node in prompt:
# print(f"debug generate_custom_name: --node.nodeKey = {node}.{nodeKey}")
# splitKey[0] = #node number found in prompt, we will recurse only in that node:
self.find_keys_recursively(prompt[node], [nodeKey], found_values)
else:
# if splitKey[0] = #num node not found in prompt; #num could have changed or user made a typo
print(f"SaveImageExtended info: node #{node} not found")
self.find_keys_recursively(prompt, [nodeKey], found_values)
else:
# key is in the form string.string = fixed string
value = key
else:
# key is in the form .string or string.
value = key
else:
# from now on we will work with nodeKey, that will save us tons of if-then-else
# nodeKey could also be = to ".smth" but that's user typo, we cannot test all scenarios
# nodeKey is not a folder, has no dot, could be a valid key to find or a fixed string
nodeKey = key
# we just try and find the last value for that key, whichever node it's in:
self.find_keys_recursively(prompt, [nodeKey], found_values)
# is key num.widget_name
# is key subfolder
# at this point we have a nodeKey but maybe no value
if value is None: value = found_values[nodeKey]
# at this point, value is neither a widget value, nor a folder, must be a simple string
# at this point we have a nodeKey, or a value, or both
# print(f"debug generate_custom_name: ----key: {nodeKey}")
# print(f"debug generate_custom_name: ----value: {value}")
if value is None:
# key not found = it's a fixed string
value = nodeKey
if nodeKey is not None:
if nodeKey in found_values: value = found_values[nodeKey]
if value is None:
value = nodeKey
# at this point, value is not None anymore
# now we analyze each value found and format them accordingly:
# print(f"debug generate_custom_name: ----key: {nodeKey}")
# print(f"debug generate_custom_name: ----value: {value}")
if isinstance(value, float):
value = round(float(value), 1)
@@ -284,7 +302,7 @@ class SaveImageExtended:
custom_name += f"{delimiter}{value}"
# print(f"debug generate_custom_name: ------custom_name: {custom_name}")
# for each key
return custom_name.strip(delimiter)
return custom_name.strip(delimiter).strip('.').strip('/').strip(delimiter)
def save_job_to_json(self, save_job_data, prompt, filename_prefix, positive_text_opt, negative_text_opt, job_custom_text, resolution, output_path, filename):
@@ -478,39 +496,53 @@ class SaveImageExtended:
# class SaveImageExtended -------------------------------------------------------------------------------
# node will never return None values, except for optional input. Impossible.
def save_images(self,
counter_digits,
counter_position,
one_counter_per_folder,
delimiter,
filename_keys,
foldername_keys,
images,
image_preview,
filename_prefix,
filename_keys,
foldername_prefix,
foldername_keys,
delimiter,
save_job_data,
job_data_per_image,
job_custom_text,
save_metadata,
filename_prefix=filename_prefix,
foldername_prefix=foldername_prefix,
extra_pnginfo=None,
counter_digits,
counter_position,
one_counter_per_folder,
image_preview,
output_ext,
negative_text_opt=None,
positive_text_opt=None,
extra_pnginfo=None,
prompt=None,
output_ext=output_ext
):
# print(f"filename_prefix = x{filename_prefix}x")
# print(f"filename_keys = x{filename_keys}x")
# print(f"foldername_prefix = x{foldername_prefix}x")
# print(f"foldername_keys = x{foldername_keys}x")
# print(f"delimiter = x{delimiter}x")
# print(f"save_job_data = x{save_job_data}x")
# print(f"job_data_per_image = x{job_data_per_image}x")
# print(f"output_ext = x{output_ext}x")
# apply default values: we replicate the default save image box
if not filename_prefix and not filename_keys: filename_prefix=self.filename_prefix
if delimiter: delimiter = delimiter[0]
filename_keys_to_extract = [item.strip() for item in filename_keys.split(',')]
foldername_keys_to_extract = [item.strip() for item in foldername_keys.split(',')]
custom_filename = self.generate_custom_name(filename_keys_to_extract, filename_prefix, delimiter, prompt)
custom_foldername = self.generate_custom_name(foldername_keys_to_extract, foldername_prefix, delimiter, prompt)
# Get set resolution value
i = 255. * images[0].cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
resolution = f'{img.width}x{img.height}'
delimiter = delimiter[0]
filename_keys_to_extract = [item.strip() for item in filename_keys.split(',')]
foldername_keys_to_extract = [item.strip() for item in foldername_keys.split(',')]
custom_filename = self.generate_custom_name(filename_keys_to_extract, filename_prefix, delimiter, resolution, prompt)
custom_foldername = self.generate_custom_name(foldername_keys_to_extract, foldername_prefix, delimiter, resolution, prompt)
# Create folders, count images, save images
try:
full_output_folder, filename, _, _, custom_filename = folder_paths.get_save_image_path(custom_filename, self.output_dir, images[0].shape[1], images[0].shape[0])