This commit is contained in:
bobmagicii
2024-04-02 15:39:29 -05:00
parent ec795ccb2d
commit 72a9a3789f
6 changed files with 221 additions and 0 deletions
View File
+21
View File
@@ -0,0 +1,21 @@
# ComfyKit Custom Nodes
## LoraWithMetadata
Accepts 3 LoRAs to blend and is able to load trigger presets from text files
named the same as the LoRA file, but with `.tp01.txt`, `.tp02.txt`, etc, instead
of the `.safetensors`.
I like to copy the trigger words out of the website, put them in those presets
and then have a text widget show me all the triggers in play so I can work them
into my prompts.
## TypecasterImage
Takes an image, returns the same image. Hard typecasted. This was to fix
problems in nodes that try too hard to be dynamic not knowing what the datatype
is when you first load a workflow.json, which required manual reconnection
to make the workflow work that first time.
TODO: make more typecasters. been abusing rg3 contexts for it but they also are
very bulky for many cases.
+3
View File
@@ -0,0 +1,3 @@
# things to ignore
**/__pycache__/**
+14
View File
@@ -0,0 +1,14 @@
from .nodes.LoraWithMeta import *
from .nodes.TypecasterImage import *
NODE_CLASS_MAPPINGS = {
"LoraWithMeta": LoraWithMeta,
"TypecasterImage": TypecasterImage
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LoraWithMeta": "LoRA+Metadata",
"Recast Image": TypecasterImage
}
__all__ = [ 'NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS' ]
+145
View File
@@ -0,0 +1,145 @@
import os
import comfy.sd
import folder_paths
class LoraWithMeta:
def __init__(self):
pass
RETURN_TYPES = ("MODEL", "CLIP", "STRING")
RETURN_NAMES = ("MODEL", "CLIP", "TRIGGERS")
FUNCTION = "onRun"
CATEGORY = "bobmagicii"
Bumper = 1
################################################################
################################################################
@classmethod
def INPUT_TYPES(s):
loraList = folder_paths.get_filename_list("loras")
loraList.insert(0, "None")
fieldPreset = ("INT", {
"default": 0, "min": 0, "max": 99,
"step": 1, "display": "number"
})
fieldStr = ("FLOAT", {
"default": 1.0, "min": -16.0, "max": 16.0,
"step": 0.1, "display": "number"
})
output = { "required": {
"model": ("MODEL", ),
"clip": ("CLIP", ),
"lora1": (loraList, ),
"lora1_tgr_preset": fieldPreset,
"lora1_str_model": fieldStr,
"lora1_str_clip": fieldStr,
"lora2": (loraList, ),
"lora2_tgr_preset": fieldPreset,
"lora2_str_model": fieldStr,
"lora2_str_clip": fieldStr,
"lora3": (loraList, ),
"lora3_tgr_preset": fieldPreset,
"lora3_str_model": fieldStr,
"lora3_str_clip": fieldStr
} }
return output
################################################################
################################################################
@classmethod
def IS_CHANGED(
self, model, clip,
lora1, lora1_tgr_preset, lora1_str_model, lora1_str_clip,
lora2, lora2_tgr_preset, lora2_str_model, lora2_str_clip,
lora3, lora3_tgr_preset, lora3_str_model, lora3_str_clip
):
##self.Bumper += self.Bumper
return self.Bumper
################################################################
################################################################
def onRun(
self, model, clip,
lora1, lora1_tgr_preset, lora1_str_model, lora1_str_clip,
lora2, lora2_tgr_preset, lora2_str_model, lora2_str_clip,
lora3, lora3_tgr_preset, lora3_str_model, lora3_str_clip
):
l1 = { "name": lora1, "tp": lora1_tgr_preset, "sm": lora1_str_model, "sc": lora1_str_clip }
l2 = { "name": lora2, "tp": lora2_tgr_preset, "sm": lora2_str_model, "sc": lora2_str_clip }
l3 = { "name": lora3, "tp": lora3_tgr_preset, "sm": lora3_str_model, "sc": lora3_str_clip }
mblend, cblend = self.applyLoraStack(
model, clip, [ l1, l2, l3 ]
)
tblend = self.fetchLoraTriggers(
[ l1, l2, l3 ]
)
return (mblend, cblend, tblend)
def applyLoraStack(self, model, clip, listOfLora):
mblend = model
cblend = clip
########
for lora in listOfLora:
if(lora['name'] == "None"):
continue
lpath = folder_paths.get_full_path("loras", lora['name'])
lfile = comfy.utils.load_torch_file(lpath, safe_load=True)
mblend, cblend = comfy.sd.load_lora_for_models(mblend, cblend, lfile, lora['sm'], lora['sc'])
print(f"[LoRA+Metadata Apply] {lpath}, {lora['sm']}, {lora['sc']}")
########
return (mblend, cblend)
def fetchLoraTriggers(self, listOfLora):
output = []
########
for lora in listOfLora:
if(lora['name'] == 'None'):
continue
if(lora['tp'] <= 0):
continue
lpath = folder_paths.get_full_path('loras', lora['name'])
lpath = lpath.replace('.safetensors', f".tp{lora['tp']:02d}.txt")
if(not os.path.isfile(lpath)):
print(f'[LoRA+Metadata Triggers] Preset Not Found: {lpath}')
continue
print(f'[LoRA+Metadata Triggers] {lpath}')
lfile = open(lpath, 'r')
output.append(lfile.readline())
lfile.close()
########
return ', '.join(output)
+38
View File
@@ -0,0 +1,38 @@
import os
import comfy.sd
import folder_paths
class TypecasterImage:
def __init__(self):
pass
RETURN_TYPES = ("IMAGE", )
RETURN_NAMES = ("IMAGE", )
FUNCTION = "onRun"
CATEGORY = "bobmagicii"
Bumper = 1
################################################################
################################################################
@classmethod
def INPUT_TYPES(s):
inputs = { "required": {
"image": ("IMAGE", )
} }
return inputs
################################################################
################################################################
def onRun(self, image):
return (image, )