Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6369fb315f | ||
|
|
fc67d2b6b0 |
@@ -48,6 +48,7 @@ git clone https://github.com/audioscavenger/save-image-extended-comfyui
|
||||
```
|
||||
|
||||
## Parameters / Usage
|
||||
|
||||
| Attribute | Description |
|
||||
| --- | --- |
|
||||
| `filename_prefix` | String prefix added to files. |
|
||||
@@ -67,6 +68,8 @@ git clone https://github.com/audioscavenger/save-image-extended-comfyui
|
||||
|
||||
Unknown key names in `filename_keys` and `foldername_keys` are treated as custom strings: if you enter `wrongNumber.attribute`, you will get `attribute` in your filename.
|
||||
|
||||
Datetime UNIX format is now included! `%Y-%m-%d` or `%F` etc
|
||||
|
||||
## Node inputs
|
||||
|
||||
- `images` - The generated images.
|
||||
@@ -75,9 +78,18 @@ Optional:
|
||||
- `positive_text_opt` - Optional string input for when using custom nodes for positive prompt text.
|
||||
- `negative_text_opt` - Optional string input for when using custom nodes for negative prompt text.
|
||||
|
||||
## Automatic folder names and date/time in names
|
||||
## Automatic date/time conversion in file/folder names
|
||||
|
||||
Converts [unix datetime formats](https://www.man7.org/linux/man-pages/man1/date.1.html):
|
||||
|
||||
| Unix datetime | Example | Comment |
|
||||
| --- | --- | --- |
|
||||
| %F or %Y-%m-%d | 2024-05-22 | |
|
||||
| %D | 05/22/24 | This effectively creates subfolders |
|
||||
| %F %H-%M-%S | 2024-05-22 09-13-58 | |
|
||||
| %Y/%V | 2024/21 | year subfolder / ISO week number subfolder |
|
||||
|
||||
|
||||
Convert the 'prefix' parameters to inputs (right click in the node and select e.g 'convert foldername_prefix to input'. Then attach the 'Get Date Time String' custom node from JPS to these inputs. This way a new folder name can be automatically generated each time generate is pressed.
|
||||
#
|
||||
Disclaimer: Does not check for illegal characters entered in file or folder names. May not be compatible with every other custom node, depending on changes in the `prompt` object.
|
||||
Tested and working with default samplers, Efficiency nodes, UltimateSDUpscale, ComfyRoll, composer, NegiTools, and 45 other nodes.
|
||||
@@ -144,6 +156,7 @@ TODO:
|
||||
|
||||
### release 2.50 💾
|
||||
- added JXL support
|
||||
- added 'BOOLEAN' support
|
||||
|
||||
### release 2.46 💾
|
||||
- bug discovered with *rgthree's Ksampler Config*: using `steps_total` as an input to a Ksampler, will issue the index of the output, instead of the steps value ("\[nodeNum, 0]" instead of steps value). FIX: use `steps_total` instead of `steps`!
|
||||
|
||||
+24
-17
@@ -44,7 +44,7 @@ original_locale = locale.setlocale(locale.LC_TIME, '')
|
||||
|
||||
# class SaveImageExtended -------------------------------------------------------------------------------
|
||||
class SaveImageExtended:
|
||||
version = 2.50
|
||||
version = 2.51
|
||||
type = 'output'
|
||||
|
||||
png_compress_level = 9
|
||||
@@ -115,9 +115,9 @@ class SaveImageExtended:
|
||||
'basic, sampler, prompt',
|
||||
'basic, models, sampler, prompt'
|
||||
], {'default': self.save_job_data}),
|
||||
'job_data_per_image': ([False, True], {'default': self.job_data_per_image}),
|
||||
'job_data_per_image': ('BOOLEAN', {"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}),
|
||||
'save_metadata': ('BOOLEAN', {'default': self.save_metadata}),
|
||||
'counter_digits': ('INT', {
|
||||
"default": self.counter_digits,
|
||||
"min": 1,
|
||||
@@ -126,8 +126,8 @@ class SaveImageExtended:
|
||||
"display": "silder"
|
||||
}),
|
||||
'counter_position': (self.counter_positions, {'default': self.counter_position}),
|
||||
'one_counter_per_folder': ([True, False], {'default': self.one_counter_per_folder}),
|
||||
'image_preview': ([True, False], {'default': self.image_preview}),
|
||||
'one_counter_per_folder': ('BOOLEAN', {'default': self.one_counter_per_folder}),
|
||||
'image_preview': ('BOOLEAN', {'default': self.image_preview}),
|
||||
'output_ext': (self.output_exts, {'default': self.output_ext}),
|
||||
},
|
||||
'optional': {
|
||||
@@ -232,7 +232,7 @@ class SaveImageExtended:
|
||||
# Fractions and Superscripts '⅔','2²' False True True
|
||||
# Roman Numerals 'ↁ' False False True
|
||||
# --------------------------- --------- ----------- --------- -----------
|
||||
def generate_custom_name(self, keys_to_extract, prefix, delimiter, prompt):
|
||||
def generate_custom_name(self, keys_to_extract, prefix, delimiter, prompt, timestamp=datetime.now()):
|
||||
custom_name = prefix
|
||||
if prompt is not None and keys_to_extract != ['']:
|
||||
found_values = {}
|
||||
@@ -252,13 +252,15 @@ class SaveImageExtended:
|
||||
node, nodeKey = None, None
|
||||
|
||||
# check if this is a subfolder: starts with ./ or /, can also end with /
|
||||
if '/' in key:
|
||||
# we also exclude datetime formats like "%A %d. %B %Y"
|
||||
if '/' in key and not '%' in key:
|
||||
# key is a subfolder: ./subfolder or ../subfolder or /subfolder
|
||||
value = key
|
||||
else:
|
||||
splitKey = key.split('.')
|
||||
# we also exclude cases like "..string" or "sting.string.x" etc
|
||||
if len(splitKey) == 2:
|
||||
# we also exclude datetime formats like "%A %d. %B %Y"
|
||||
if len(splitKey) == 2 and not '%' in key:
|
||||
# key has the form string.string
|
||||
if '' not in splitKey:
|
||||
# key has the form string.string
|
||||
@@ -280,9 +282,13 @@ class SaveImageExtended:
|
||||
# key is in the form ".string" or "string." or "." - we won't clean that up and keep as is, maybe it's a separator
|
||||
value = key
|
||||
else:
|
||||
# nodeKey is not a folder, has no dot, or multiple dots, could be a valid key to find, could be a fixed string - keep as is
|
||||
nodeKey = key
|
||||
self.find_keys_recursively(prompt, [nodeKey], found_values)
|
||||
# could be a datetime format
|
||||
if '%' in key:
|
||||
value = timestamp.strftime(key)
|
||||
# nodeKey is not a datetime, folder, has no dot, or multiple dots, could be a valid key to find, could be a fixed string - keep as is
|
||||
else:
|
||||
nodeKey = key
|
||||
self.find_keys_recursively(prompt, [nodeKey], found_values)
|
||||
# is key num.widget_name
|
||||
# is key subfolder
|
||||
|
||||
@@ -326,7 +332,7 @@ class SaveImageExtended:
|
||||
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):
|
||||
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, timestamp=datetime.now()):
|
||||
prompt_keys_to_save = {}
|
||||
if 'basic' in save_job_data:
|
||||
if len(filename_prefix) > 0:
|
||||
@@ -411,7 +417,7 @@ class SaveImageExtended:
|
||||
print(f"SaveImageExtended {version} error: The file {json_file_path} is empty or malformed. Initializing with empty data.")
|
||||
existing_data = {}
|
||||
|
||||
timestamp = datetime.now().strftime('%c')
|
||||
timestamp = timestamp.strftime('%c')
|
||||
new_entry = {}
|
||||
new_entry[timestamp] = prompt_keys_to_save
|
||||
existing_data.update(new_entry)
|
||||
@@ -568,8 +574,9 @@ class SaveImageExtended:
|
||||
################################## UNCOMMENT HERE TO SEE THE ENTIRE PROMPT
|
||||
# pprint.pprint(prompt)
|
||||
##########################################################################
|
||||
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)
|
||||
timestamp = datetime.now()
|
||||
custom_filename = self.generate_custom_name(filename_keys_to_extract, filename_prefix, delimiter, prompt, timestamp)
|
||||
custom_foldername = self.generate_custom_name(foldername_keys_to_extract, foldername_prefix, delimiter, prompt, timestamp)
|
||||
|
||||
# Get set resolution value
|
||||
i = 255. * images[0].cpu().numpy()
|
||||
@@ -601,14 +608,14 @@ class SaveImageExtended:
|
||||
self.save_image(image_path, img, prompt, save_metadata, extra_pnginfo)
|
||||
|
||||
if save_job_data != 'disabled' and job_data_per_image:
|
||||
self.save_job_to_json(save_job_data, prompt, filename_prefix, positive_text_opt, negative_text_opt, job_custom_text, resolution, output_path, f'{file.removesuffix(output_ext)}.json')
|
||||
self.save_job_to_json(save_job_data, prompt, filename_prefix, positive_text_opt, negative_text_opt, job_custom_text, resolution, output_path, f'{file.removesuffix(output_ext)}.json', timestamp)
|
||||
|
||||
subfolder = self.get_subfolder_path(image_path, self.output_dir)
|
||||
results.append({ 'filename': file, 'subfolder': subfolder, 'type': self.type})
|
||||
counter += 1
|
||||
|
||||
if save_job_data != 'disabled' and not job_data_per_image:
|
||||
self.save_job_to_json(save_job_data, prompt, filename_prefix, positive_text_opt, negative_text_opt, job_custom_text, resolution, output_path, 'jobs.json')
|
||||
self.save_job_to_json(save_job_data, prompt, filename_prefix, positive_text_opt, negative_text_opt, job_custom_text, resolution, output_path, 'jobs.json', timestamp)
|
||||
|
||||
except OSError as e:
|
||||
print(f"SaveImageExtended {version} error: An error occurred while creating the subfolder or saving the image: {e}")
|
||||
|
||||
Reference in New Issue
Block a user