mirror of
https://github.com/storytold/storyteller-ml.git
synced 2026-10-25 08:40:31 +00:00
remove old comfyUI
This commit is contained in:
@@ -1 +0,0 @@
|
||||
__pycache__
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Girish Gopaul
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,27 +0,0 @@
|
||||
# Save image with generation metadata on ComfyUI
|
||||
|
||||
All the tools you need to save images with their **generation metadata** on ComfyUI. Compatible with *Civitai* & *Prompthero* geninfo auto-detection. Works with `png`, `jpeg` and `webp`.
|
||||
|
||||
You can find the example workflow file named `example-workflow.json`.
|
||||
|
||||

|
||||
|
||||
## How to install?
|
||||
|
||||
### Method 1: Easiest (Recommended)
|
||||
If you have *ComfyUI-Manager*, you can simply search "**Save Image with Generation Metadata**" and install these custom nodes 🎉
|
||||
|
||||
|
||||
### Method 2: Easy
|
||||
If you don't have *ComfyUI-Manager*, then:
|
||||
- Using CLI, go to the ComfyUI folder
|
||||
- `cd custom_nodes`
|
||||
- `git clone git@github.com:giriss/comfy-image-saver.git`
|
||||
- `cd comfy-image-saver`
|
||||
- `pip install -r requirements.txt`
|
||||
- Start/restart ComfyUI 🎉
|
||||
|
||||
## Autodetection in action
|
||||
|
||||

|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
from .nodes import NODE_CLASS_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS']
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,279 +0,0 @@
|
||||
import os
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
import json
|
||||
import piexif
|
||||
import piexif.helper
|
||||
from PIL import Image, ExifTags
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import numpy as np
|
||||
import folder_paths
|
||||
import comfy.sd
|
||||
from nodes import MAX_RESOLUTION
|
||||
|
||||
|
||||
def parse_name(ckpt_name):
|
||||
path = ckpt_name
|
||||
filename = path.split("/")[-1]
|
||||
filename = filename.split(".")[:-1]
|
||||
filename = ".".join(filename)
|
||||
return filename
|
||||
|
||||
|
||||
def calculate_sha256(file_path):
|
||||
sha256_hash = hashlib.sha256()
|
||||
|
||||
with open(file_path, "rb") as f:
|
||||
# Read the file in chunks to avoid loading the entire file into memory
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
def handle_whitespace(string: str):
|
||||
return string.strip().replace("\n", " ").replace("\r", " ").replace("\t", " ")
|
||||
|
||||
|
||||
def get_timestamp(time_format):
|
||||
now = datetime.now()
|
||||
try:
|
||||
timestamp = now.strftime(time_format)
|
||||
except:
|
||||
timestamp = now.strftime("%Y-%m-%d-%H%M%S")
|
||||
|
||||
return timestamp
|
||||
|
||||
|
||||
def make_pathname(filename, seed, modelname, counter, time_format):
|
||||
filename = filename.replace("%date", get_timestamp("%Y-%m-%d"))
|
||||
filename = filename.replace("%time", get_timestamp(time_format))
|
||||
filename = filename.replace("%model", modelname)
|
||||
filename = filename.replace("%seed", str(seed))
|
||||
filename = filename.replace("%counter", str(counter))
|
||||
return filename
|
||||
|
||||
|
||||
def make_filename(filename, seed, modelname, counter, time_format):
|
||||
filename = make_pathname(filename, seed, modelname, counter, time_format)
|
||||
|
||||
return get_timestamp(time_format) if filename == "" else filename
|
||||
|
||||
|
||||
class SeedGenerator:
|
||||
RETURN_TYPES = ("INT",)
|
||||
FUNCTION = "get_seed"
|
||||
CATEGORY = "ImageSaverTools/utils"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff})}}
|
||||
|
||||
def get_seed(self, seed):
|
||||
return (seed,)
|
||||
|
||||
|
||||
class StringLiteral:
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "get_string"
|
||||
CATEGORY = "ImageSaverTools/utils"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"string": ("STRING", {"default": "", "multiline": True})}}
|
||||
|
||||
def get_string(self, string):
|
||||
return (string,)
|
||||
|
||||
|
||||
class SizeLiteral:
|
||||
RETURN_TYPES = ("INT",)
|
||||
FUNCTION = "get_int"
|
||||
CATEGORY = "ImageSaverTools/utils"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"int": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 8})}}
|
||||
|
||||
def get_int(self, int):
|
||||
return (int,)
|
||||
|
||||
|
||||
class IntLiteral:
|
||||
RETURN_TYPES = ("INT",)
|
||||
FUNCTION = "get_int"
|
||||
CATEGORY = "ImageSaverTools/utils"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"int": ("INT", {"default": 0, "min": 0, "max": 1000000})}}
|
||||
|
||||
def get_int(self, int):
|
||||
return (int,)
|
||||
|
||||
|
||||
class CfgLiteral:
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
FUNCTION = "get_float"
|
||||
CATEGORY = "ImageSaverTools/utils"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"float": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0})}}
|
||||
|
||||
def get_float(self, float):
|
||||
return (float,)
|
||||
|
||||
|
||||
class CheckpointSelector:
|
||||
CATEGORY = 'ImageSaverTools/utils'
|
||||
RETURN_TYPES = (folder_paths.get_filename_list("checkpoints"),)
|
||||
RETURN_NAMES = ("ckpt_name",)
|
||||
FUNCTION = "get_names"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"ckpt_name": (folder_paths.get_filename_list("checkpoints"), ),}}
|
||||
|
||||
def get_names(self, ckpt_name):
|
||||
return (ckpt_name,)
|
||||
|
||||
|
||||
class SamplerSelector:
|
||||
CATEGORY = 'ImageSaverTools/utils'
|
||||
RETURN_TYPES = (comfy.samplers.KSampler.SAMPLERS,)
|
||||
RETURN_NAMES = ("sampler_name",)
|
||||
FUNCTION = "get_names"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"sampler_name": (comfy.samplers.KSampler.SAMPLERS,)}}
|
||||
|
||||
def get_names(self, sampler_name):
|
||||
return (sampler_name,)
|
||||
|
||||
|
||||
class SchedulerSelector:
|
||||
CATEGORY = 'ImageSaverTools/utils'
|
||||
RETURN_TYPES = (comfy.samplers.KSampler.SCHEDULERS,)
|
||||
RETURN_NAMES = ("scheduler",)
|
||||
FUNCTION = "get_names"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {"scheduler": (comfy.samplers.KSampler.SCHEDULERS,)}}
|
||||
|
||||
def get_names(self, scheduler):
|
||||
return (scheduler,)
|
||||
|
||||
|
||||
class ImageSaveWithMetadata:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.output_directory
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE", ),
|
||||
"filename": ("STRING", {"default": f'%time_%seed', "multiline": False}),
|
||||
"path": ("STRING", {"default": '', "multiline": False}),
|
||||
"extension": (['png', 'jpeg', 'webp'],),
|
||||
"steps": ("INT", {"default": 20, "min": 1, "max": 10000}),
|
||||
"cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0}),
|
||||
"modelname": (folder_paths.get_filename_list("checkpoints"),),
|
||||
"sampler_name": (comfy.samplers.KSampler.SAMPLERS,),
|
||||
"scheduler": (comfy.samplers.KSampler.SCHEDULERS,),
|
||||
},
|
||||
"optional": {
|
||||
"positive": ("STRING", {"default": 'unknown', "multiline": True}),
|
||||
"negative": ("STRING", {"default": 'unknown', "multiline": True}),
|
||||
"seed_value": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"width": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"height": ("INT", {"default": 512, "min": 1, "max": MAX_RESOLUTION, "step": 8}),
|
||||
"lossless_webp": ("BOOLEAN", {"default": True}),
|
||||
"quality_jpeg_or_webp": ("INT", {"default": 100, "min": 1, "max": 100}),
|
||||
"counter": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff }),
|
||||
"time_format": ("STRING", {"default": "%Y-%m-%d-%H%M%S", "multiline": False}),
|
||||
},
|
||||
"hidden": {
|
||||
"prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO"
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "save_files"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "ImageSaverTools"
|
||||
|
||||
def save_files(self, images, seed_value, steps, cfg, sampler_name, scheduler, positive, negative, modelname, quality_jpeg_or_webp,
|
||||
lossless_webp, width, height, counter, filename, path, extension, time_format, prompt=None, extra_pnginfo=None):
|
||||
filename = make_filename(filename, seed_value, modelname, counter, time_format)
|
||||
path = make_pathname(path, seed_value, modelname, counter, time_format)
|
||||
ckpt_path = folder_paths.get_full_path("checkpoints", modelname)
|
||||
basemodelname = parse_name(modelname)
|
||||
modelhash = calculate_sha256(ckpt_path)[:10]
|
||||
comment = f"{handle_whitespace(positive)}\nNegative prompt: {handle_whitespace(negative)}\nSteps: {steps}, Sampler: {sampler_name}{f'_{scheduler}' if scheduler != 'normal' else ''}, CFG Scale: {cfg}, Seed: {seed_value}, Size: {width}x{height}, Model hash: {modelhash}, Model: {basemodelname}, Version: ComfyUI"
|
||||
output_path = os.path.join(self.output_dir, path)
|
||||
|
||||
if output_path.strip() != '':
|
||||
if not os.path.exists(output_path.strip()):
|
||||
print(f'The path `{output_path.strip()}` specified doesn\'t exist! Creating directory.')
|
||||
os.makedirs(output_path, exist_ok=True)
|
||||
|
||||
filenames = self.save_images(images, output_path, filename, comment, extension, quality_jpeg_or_webp, lossless_webp, prompt, extra_pnginfo)
|
||||
|
||||
subfolder = os.path.normpath(path)
|
||||
return {"ui": {"images": map(lambda filename: {"filename": filename, "subfolder": subfolder if subfolder != '.' else '', "type": 'output'}, filenames)}}
|
||||
|
||||
def save_images(self, images, output_path, filename_prefix, comment, extension, quality_jpeg_or_webp, lossless_webp, prompt=None, extra_pnginfo=None) -> list[str]:
|
||||
img_count = 1
|
||||
paths = list()
|
||||
for image in images:
|
||||
i = 255. * image.cpu().numpy()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
if images.size()[0] > 1:
|
||||
filename_prefix += "_{:02d}".format(img_count)
|
||||
|
||||
if extension == 'png':
|
||||
metadata = PngInfo()
|
||||
metadata.add_text("parameters", comment)
|
||||
|
||||
if prompt is not None:
|
||||
metadata.add_text("prompt", json.dumps(prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
|
||||
filename = f"{filename_prefix}.png"
|
||||
img.save(os.path.join(output_path, filename), pnginfo=metadata, optimize=True)
|
||||
else:
|
||||
filename = f"{filename_prefix}.{extension}"
|
||||
file = os.path.join(output_path, filename)
|
||||
img.save(file, optimize=True, quality=quality_jpeg_or_webp, lossless=lossless_webp)
|
||||
exif_bytes = piexif.dump({
|
||||
"Exif": {
|
||||
piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(comment, encoding="unicode")
|
||||
},
|
||||
})
|
||||
piexif.insert(exif_bytes, file)
|
||||
|
||||
paths.append(filename)
|
||||
img_count += 1
|
||||
return paths
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"Checkpoint Selector": CheckpointSelector,
|
||||
"Save Image w/Metadata": ImageSaveWithMetadata,
|
||||
"Sampler Selector": SamplerSelector,
|
||||
"Scheduler Selector": SchedulerSelector,
|
||||
"Seed Generator": SeedGenerator,
|
||||
"String Literal": StringLiteral,
|
||||
"Width/Height Literal": SizeLiteral,
|
||||
"Cfg Literal": CfgLiteral,
|
||||
"Int Literal": IntLiteral,
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
piexif
|
||||
Reference in New Issue
Block a user