154 lines
6.2 KiB
Python
154 lines
6.2 KiB
Python
import os
|
|
import folder_paths
|
|
import torch
|
|
|
|
class MultiLoraLoader:
|
|
# Class variable to store LoRA list
|
|
all_loras = []
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
# Get all available lora directories and gather all lora files
|
|
cls.all_loras = []
|
|
lora_dirs = folder_paths.get_folder_paths("loras")
|
|
|
|
for lora_dir in lora_dirs:
|
|
if os.path.isdir(lora_dir):
|
|
for filename in os.listdir(lora_dir):
|
|
if filename.endswith(('.safetensors', '.ckpt', '.pt')):
|
|
cls.all_loras.append(filename)
|
|
|
|
# Sort and remove duplicates
|
|
cls.all_loras = sorted(list(set(cls.all_loras)))
|
|
|
|
# Print debug info
|
|
print(f"Found {len(cls.all_loras)} LoRA files in {len(lora_dirs)} directories")
|
|
|
|
max_index = max(0, len(cls.all_loras) - 1)
|
|
|
|
return {
|
|
"required": {
|
|
"model": ("MODEL",),
|
|
"index": ("INT", {"default": 0, "min": 0, "max": max_index, "step": 1}),
|
|
"lora_list": (cls.all_loras, {"default": cls.all_loras[0] if cls.all_loras else ""}),
|
|
"strength_model": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
|
"strength_clip": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),
|
|
},
|
|
"optional": {
|
|
"clip": ("CLIP", ),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("MODEL", "CLIP")
|
|
FUNCTION = "load_lora"
|
|
CATEGORY = "loaders"
|
|
|
|
def load_lora(self, model, index, lora_list, strength_model, strength_clip, clip=None):
|
|
lora_dirs = folder_paths.get_folder_paths("loras")
|
|
|
|
# Use the class variable to access the LoRA list
|
|
all_loras = self.__class__.all_loras
|
|
|
|
# Validate we have LoRAs and index is in range
|
|
if len(all_loras) == 0:
|
|
print("No LoRAs found. Returning model without changes.")
|
|
return (model, clip)
|
|
|
|
if index < 0 or index >= len(all_loras):
|
|
print(f"LoRA index {index} out of range (0-{len(all_loras)-1}). Using index 0.")
|
|
index = 0
|
|
|
|
# Get the LoRA file name based on index
|
|
lora_name = all_loras[index]
|
|
|
|
# Find the LoRA file in any of the lora directories
|
|
lora_path = None
|
|
for lora_dir in lora_dirs:
|
|
potential_path = os.path.join(lora_dir, lora_name)
|
|
if os.path.isfile(potential_path):
|
|
lora_path = potential_path
|
|
break
|
|
|
|
# If we couldn't find the file
|
|
if lora_path is None:
|
|
print(f"LoRA file '{lora_name}' not found in any LoRA directories. Returning model without changes.")
|
|
return (model, clip)
|
|
|
|
print(f"Loading LoRA {lora_name} (index {index}/{len(all_loras)-1}) from {lora_path}")
|
|
print(f"Model strength: {strength_model}, Clip strength: {strength_clip}")
|
|
|
|
# Import the reference implementation from ComfyUI's nodes
|
|
try:
|
|
import importlib.util
|
|
import sys
|
|
|
|
# Get the ComfyUI nodes directory
|
|
script_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
comfy_dir = os.path.join(script_dir, "comfy")
|
|
nodes_dir = os.path.join(script_dir, "comfy_extras")
|
|
|
|
if os.path.exists(os.path.join(comfy_dir, "sd.py")) and os.path.exists(os.path.join(nodes_dir, "nodes_lora.py")):
|
|
# Load the lora module directly from ComfyUI
|
|
spec = importlib.util.spec_from_file_location("nodes_lora", os.path.join(nodes_dir, "nodes_lora.py"))
|
|
nodes_lora = importlib.util.module_from_spec(spec)
|
|
sys.modules["nodes_lora"] = nodes_lora
|
|
spec.loader.exec_module(nodes_lora)
|
|
|
|
# Use the reference implementation
|
|
lora_loader = nodes_lora.LoraLoader()
|
|
return lora_loader.load_lora(model, lora_path, strength_model, strength_clip, clip)
|
|
else:
|
|
print("Could not find ComfyUI's nodes_lora.py, falling back to internal implementation")
|
|
|
|
except Exception as e:
|
|
print(f"Error loading ComfyUI's LoraLoader: {str(e)}")
|
|
|
|
# If we couldn't use the reference implementation, use our own
|
|
try:
|
|
import comfy.utils
|
|
|
|
# Load the LoRA file
|
|
lora_sd = comfy.utils.load_torch_file(lora_path)
|
|
|
|
# We need to import these from comfy's sd module
|
|
import comfy.sd as sd
|
|
|
|
# Apply the LoRA
|
|
if hasattr(sd, 'load_lora_for_models'):
|
|
return sd.load_lora_for_models(model, clip, lora_sd, strength_model, strength_clip)
|
|
else:
|
|
model_result = model
|
|
clip_result = clip
|
|
|
|
if hasattr(sd, 'apply_weighted_lora'):
|
|
# Newer ComfyUI versions
|
|
if model is not None:
|
|
model_result = sd.apply_weighted_lora(model, lora_sd, strength_model)
|
|
if clip is not None:
|
|
clip_result = sd.apply_weighted_lora(clip, lora_sd, strength_clip)
|
|
elif hasattr(sd, 'apply_lora'):
|
|
# Older ComfyUI versions
|
|
if model is not None:
|
|
model_result = sd.apply_lora(model, lora_sd, strength_model)
|
|
if clip is not None:
|
|
clip_result = sd.apply_lora(clip, lora_sd, strength_clip)
|
|
else:
|
|
print("Could not find apply_lora or apply_weighted_lora in ComfyUI's sd module")
|
|
|
|
return (model_result, clip_result)
|
|
|
|
except Exception as e:
|
|
print(f"Error loading or applying LoRA: {str(e)}")
|
|
|
|
# If all else fails, return the original models
|
|
print("All LoRA loading methods failed, returning original model")
|
|
return (model, clip)
|
|
|
|
# Add the node to ComfyUI
|
|
NODE_CLASS_MAPPINGS = {
|
|
"MultiLoraLoader": MultiLoraLoader
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"MultiLoraLoader": "LoRA Loader (by Index)"
|
|
} |