Tested and Civitai can read both model and lora info!
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
If you are reading this, feel free to open an issue, as I should update this before changing this from private to public.
|
||||
This is the Sage Utils set of custom nodes. Everyone seems to be making their own suites of nodes, and I decided I'd go ahead and make one myself, for any nodes I feel would be helpful.
|
||||
|
||||
This is the Sage Utils set of custom nodes. Basically just where I'm going to be sticking all the nodes that I create for the moment.
|
||||
I've started with a bunch of metadata nodes. There are several other node sets out there for adding metadata for interoperability with Civitai, but I wasn't happy with them and decided to try spinning my own, though I definitely looked at ImageSaver and other places.
|
||||
|
||||
I intend to make some nodes to help with managing metadata, first of all, as I'mn not really happy with the existing ones I've found. I'd also like to have some nodes for working with Civitai, and possibly image websites apis.
|
||||
Anything dealing with Lora Stacks is intended to be interoperable with other custom nodes that use those, though I'm also spinning my own versions of a few nodes so that you don't need to use other sets nodes with mine.
|
||||
@@ -28,6 +28,43 @@ def lora_stack_to_string(stack):
|
||||
|
||||
return lora_string
|
||||
|
||||
def get_file_sha256(path):
|
||||
m = hashlib.sha256()
|
||||
with open(path, 'rb') as f:
|
||||
m.update(f.read())
|
||||
result = str(m.digest().hex()[:10])
|
||||
return result
|
||||
|
||||
class Sage_DualCLIPTextEncode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"clip": ("CLIP", {"defaultInput": True, "tooltip": "The CLIP model used for encoding the text."}),
|
||||
"pos": ("STRING", {"defaultInput": True, "default": "score_9, score_8_up, scoure_7_up, score_6_up, score_5_up, score_4_up", "multiline": True, "dynamicPrompts": True, "tooltip": "The positive prompt's text."}),
|
||||
"neg": ("STRING", {"defaultInput": True, "default": "signature, watermark", "multiline": True, "dynamicPrompts": True, "tooltip": "The negative prompt's text."})
|
||||
}
|
||||
}
|
||||
RETURN_TYPES = ("CONDITIONING", "CONDITIONING", "STRING", "STRING")
|
||||
RETURN_NAMES = ("pos_cond", "neg_cond", "pos_text", "neg_text")
|
||||
|
||||
OUTPUT_TOOLTIPS = ("A conditioning containing the embedded text used to guide the diffusion model.",)
|
||||
FUNCTION = "encode"
|
||||
|
||||
CATEGORY = "Sage Utils"
|
||||
DESCRIPTION = "Turns a positive and negative prompt into to conditionings, to save space."
|
||||
|
||||
def encode(self, clip, pos, neg):
|
||||
pos_tokens = clip.tokenize(pos)
|
||||
pos_output = clip.encode_from_tokens(pos_tokens, return_pooled=True, return_dict=True)
|
||||
pos_cond = pos_output.pop("cond")
|
||||
|
||||
neg_tokens = clip.tokenize(neg)
|
||||
neg_output = clip.encode_from_tokens(neg_tokens, return_pooled=True, return_dict=True)
|
||||
neg_cond = neg_output.pop("cond")
|
||||
|
||||
return ([[pos_cond, pos_output]], [[neg_cond, neg_output]], pos, neg)
|
||||
|
||||
class Sage_SamplerInfo:
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -84,7 +121,36 @@ class Sage_KSampler:
|
||||
def sample(self, model, sampler_info, positive, negative, latent_image, denoise=1.0):
|
||||
return nodes.common_ksampler(model, sampler_info["seed"], sampler_info["steps"], sampler_info["cfg"], sampler_info["sampler"], sampler_info["scheduler"], positive, negative, latent_image, denoise=denoise)
|
||||
|
||||
|
||||
def civitai_sampler_name(sampler_name, scheduler_name):
|
||||
comfy_to_auto = {
|
||||
'ddim': 'DDIM',
|
||||
'dpm_2': 'DPM2',
|
||||
'dpm_2_ancestral': 'DPM2 a',
|
||||
'dpmpp_2s_ancestral': 'DPM++ 2S a',
|
||||
'dpmpp_2m': 'DPM++ 2M',
|
||||
'dpmpp_sde': 'DPM++ SDE',
|
||||
'dpmpp_2m_sde': 'DPM++ 2M SDE',
|
||||
'dpmpp_3m_sde': 'DPM++ 3M SDE',
|
||||
'dpm_fast': 'DPM fast',
|
||||
'dpm_adaptive': 'DPM adaptive',
|
||||
'euler_ancestral': 'Euler a',
|
||||
'euler': 'Euler',
|
||||
'heun': 'Heun',
|
||||
'lcm': 'LCM',
|
||||
'lms': 'LMS',
|
||||
'plms': 'PLMS',
|
||||
'uni_pc': 'UniPC',
|
||||
'uni_pc_bh2': 'UniPC'
|
||||
}
|
||||
result = comfy_to_auto[sampler_name]
|
||||
|
||||
if (scheduler_name == "karras"):
|
||||
result += " Karras"
|
||||
elif (scheduler_name == "exponential"):
|
||||
result += " Exponential"
|
||||
|
||||
return result
|
||||
|
||||
class Sage_ConstructMetadata:
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -116,17 +182,26 @@ class Sage_ConstructMetadata:
|
||||
metadata = ''
|
||||
lora_info = ''
|
||||
resource_hashes = {}
|
||||
lora_hashes = ', Lora hashes: '
|
||||
|
||||
resource_hashes['model'] = model_info['hash']
|
||||
sampler_name = civitai_sampler_name(sampler_info['sampler'], sampler_info['scheduler'])
|
||||
|
||||
if lora_stack is None:
|
||||
lora_info = ""
|
||||
else:
|
||||
lora_info = lora_stack_to_string(lora_stack)
|
||||
|
||||
resource_hashes['model'] = model_info['hash']
|
||||
for lora in lora_stack:
|
||||
lora_info += lora_to_string(lora[0], lora[1], lora[2])
|
||||
lora_path = folder_paths.get_full_path_or_raise("loras", lora[0])
|
||||
lora_name = str(pathlib.Path(lora_path).name)
|
||||
lora_hash = get_file_sha256(lora_path)
|
||||
resource_hashes[f"lora:{lora_name}"] = lora_hash
|
||||
lora_hashes += f"{lora_name}: {lora_hash},"
|
||||
|
||||
metadata = f"{positive_string} {lora_info}" + "\n"
|
||||
metadata += f"Negative prompt: {negative_string}" + "\n"
|
||||
metadata += f"Steps: {sampler_info['steps']}, Sampler: {sampler_info['sampler']}, Scheduler type: {sampler_info['scheduler']}, CFG scale: {sampler_info['cfg']}, Seed: {sampler_info['seed']}, Size: {width}x{height},"
|
||||
metadata += f"Model: {model_info['name']},Model hash: {model_info['hash']}, Version: v1.10-RC-6-actually-totally-comfyui, Hashes: {json.dumps(resource_hashes)}"
|
||||
metadata += f"Steps: {sampler_info['steps']}, Sampler: {sampler_name}, Scheduler type: {sampler_info['scheduler']}, CFG scale: {sampler_info['cfg']}, Seed: {sampler_info['seed']}, Size: {width}x{height},"
|
||||
metadata += f"Model: {model_info['name']}, Model hash: {model_info['hash']}, Version: v1.10-RC-6-actually-totally-comfyui, Hashes: {json.dumps(resource_hashes)}{lora_hashes}"
|
||||
print(metadata)
|
||||
return metadata,
|
||||
|
||||
@@ -156,11 +231,7 @@ class Sage_CheckpointLoaderSimple:
|
||||
model_info = { "full_name": ckpt_name }
|
||||
model_info["path"] = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)
|
||||
model_info["name"] = pathlib.Path(model_info["full_name"]).name
|
||||
|
||||
m = hashlib.sha256()
|
||||
with open(model_info["path"], 'rb') as f:
|
||||
m.update(f.read())
|
||||
model_info["hash"] = str(m.digest().hex()[:10])
|
||||
model_info["hash"] = get_file_sha256(model_info["path"])
|
||||
|
||||
out = comfy.sd.load_checkpoint_guess_config(model_info["path"], output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
||||
result = (*out[:3], model_info)
|
||||
@@ -336,6 +407,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
#"Sage_MultiModelNameChooser": Sage_MultiModelNameChooser,
|
||||
"Sage_LoraStackToString": Sage_LoraStackToString,
|
||||
"Sage_LoraNameToString": Sage_LoraNameToString,
|
||||
"Sage_DualCLIPTextEncode": Sage_DualCLIPTextEncode,
|
||||
"Sage_SamplerInfo": Sage_SamplerInfo,
|
||||
"Sage_KSampler": Sage_KSampler,
|
||||
"Sage_ConstructMetadata": Sage_ConstructMetadata,
|
||||
@@ -349,6 +421,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
#"Sage_MultiModelNameChooser": "Multi Model Name Chooser",
|
||||
"Sage_LoraStackToString": "Lora Stack to String",
|
||||
"Sage_LoraNameToString": "Lora Name to String",
|
||||
"Sage_DualCLIPTextEncode": "Prompts to CLIP",
|
||||
"Sage_SamplerInfo": "Sampler Info",
|
||||
"Sage_KSampler": "KSampler w/ Sampler Info",
|
||||
"Sage_ConstructMetadata": "Construct Metadata",
|
||||
|
||||
Reference in New Issue
Block a user