diff --git a/LoadTempMultiLoRA.py b/LoadTempMultiLoRA.py new file mode 100644 index 0000000..a07fe6d --- /dev/null +++ b/LoadTempMultiLoRA.py @@ -0,0 +1,103 @@ +import comfy.sd +import comfy.utils +import comfy.clip_vision +import comfy.model_detection +import comfy.model_management +import comfy.model_patcher +import comfy.checkpoint_pickle + +import folder_paths + +from .utils import download_file, load_torch_bin + +class LoadTempMultiLoRA: + CKPT_TYPE = ["auto", "safetensors", "other"] + def __init__(self): + self.loaded_lora = {} + + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "model": ("MODEL",), + "clip": ("CLIP", ), + "ckpt_url": ("STRING", {"default": "", "multiline": True, "dynamicPrompts": False}), + "ckpt_type": (LoadTempMultiLoRA.CKPT_TYPE, {"default": "auto"}), + "download_split": ("INT", {"default": 4, "min": 1, "max": 8, "step": 1}), + "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), + }, + } + RETURN_TYPES = ("MODEL", "CLIP") + FUNCTION = "load_multi_lora" + + CATEGORY = "temporary_loaders" + + def load_multi_lora(self, model, clip, ckpt_url, ckpt_type, download_split, strength_model, strength_clip): + model_lora, clip_lora = model, clip + lora_dic = {} + for line in ckpt_url.splitlines(): + if line.startswith("#"): + continue + else: + info = line.split(":") + if len(info) < 2: + continue + if line.startswith("file:"): + file_name = line[5:] + model_lora, clip_lora = self.load_lora_file(model_lora, clip_lora, file_name, strength_model, strength_clip, lora_dic) + continue + if line.startswith("http:") or line.startswith("https:"): + url = "{}:{}".format(info[0].strip(), info[1].strip()) + model_lora, clip_lora = self.load_lora_url(model_lora, clip_lora, url, ckpt_type, download_split, strength_model, strength_clip, lora_dic) + continue + try: + isFile = info[-2].strip().startswith("file") + url = info[-1].strip() if isFile else "{}:{}".format(info[-2].strip(), info[-1].strip()) + strength_m = float(info[0]) if len(info[0]) > 0 else strength_model + strength_c = float(info[1]) if len(info[1]) > 0 else strength_clip + m_type = info[2].strip() if len(info) == 5 and info[2].strip() in LoadTempMultiLoRA.CKPT_TYPE else ckpt_type + except: + raise Exception("Not according to format. The only accepted formats are\r\n{url}\r\n{strength_model}:{strength_clip}:{url}\r\n{strength_model}:{strength_clip}:{ckpt_type}:{url}") + if isFile: + model_lora, clip_lora = self.load_lora_file(model_lora, clip_lora, url, strength_m, strength_c, lora_dic) + else: + model_lora, clip_lora = self.load_lora_url(model_lora, clip_lora, url, m_type, download_split, strength_m, strength_c, lora_dic) + + tmp = self.loaded_lora + self.loaded_lora = lora_dic + tmp.clear() + del tmp + + return (model_lora, clip_lora) + + def load_lora_url(self, model, clip, ckpt_url, ckpt_type, download_split, strength_model, strength_clip, lora_dic): + if strength_model == 0 and strength_clip == 0: + return (model, clip) + + lora = self.loaded_lora.get(ckpt_url) + if lora is None: + bin, file_name = download_file(ckpt_url, download_split) + if bin is None: + raise file_name if file_name is not None else Exception("Download failed.") + + is_safetensors = file_name.endswith(".safetensors") if ckpt_type =="auto" else ckpt_type == "safetensors" + lora = load_torch_bin(bin, is_safetensors, safe_load=True) + + lora_dic[ckpt_url] = lora + + model_lora, clip_lora = comfy.sd.load_lora_for_models(model, clip, lora, strength_model, strength_clip) + return (model_lora, clip_lora) + + def load_lora_file(self, model, clip, lora_name, strength_model, strength_clip, lora_dic): + if strength_model == 0 and strength_clip == 0: + return (model, clip) + + lora_path = folder_paths.get_full_path("loras", lora_name) + lora = self.loaded_lora.get(lora_path) + if lora is None: + lora = comfy.utils.load_torch_file(lora_path, safe_load=True) + lora_dic[lora_path] = lora + + model_lora, clip_lora = comfy.sd.load_lora_for_models(model, clip, lora, strength_model, strength_clip) + return (model_lora, clip_lora) diff --git a/README-ja.md b/README-ja.md new file mode 100644 index 0000000..566dbb8 --- /dev/null +++ b/README-ja.md @@ -0,0 +1,46 @@ + +# ComfyUI-TemporaryLoader +入力されたURLからモデルをダウンロードして読み込むComfyUIのカスタムノードです。 + +モデルは一時的にメモリにダウンロードされ、ストレージには保存されません。 + +## Installation +1. ComfyUIのcustom_nodesディレクトリで `git clone https://github.com/pkpkTech/ComfyUI-TemporaryLoader` を実行 +1. ComfyUI-TemporaryLoaderディレクトリに移動して `pip install -r requirements.txt` を実行 + +## Nodes +"temporary_loader" カテゴリに、"Load Checkpoint (Temporary)" と "Load LoRA (Temporary)" ノードが追加されます。 + +標準のLoadノードに以下の項目が追加されています。 +- ckpt_url: 使用するモデルのURL。 +- ckpt_type: モデルの種類。`auto`でダウンロードしたファイルの拡張子で自動的に判断されますが、ファイル名が取得できない場合もあるので、その時に`safetensors`か`ohter`かを手動で選択してください。 +- download_split: 並列ダウンロードの分割数。 + +これらに加えて、"Load Multi LoRA (Temporary)"ノードも追加されます。
+これは、複数のLoRAを読み込むためのノードです。
+URL、または標準のLoRA Loaderと同じようにファイル名を指定して読み込みます。
+下記のフォーマットに従って記述してください + +`{LoRA URL}` or `file:{LoRA file name}` - 例) `https://example.com/anylora.safetensors` or `file:anylora.safetensors` + +`{strength_model}:{strength_clip}:{LoRA URL} or file:{LoRA file name}` - 例) `0.4:1.0:https://example.com/anylora.safetensors` + +`{strength_model}:{strength_clip}:{ckpt_type}:{LoRA URL} or file:{LoRA file name}` - 例) `0.4:1.0:other:https://example.com/anylora.unknownext` + +strength_model、strength_clip、ckpt_typeをテキストで指定していないLoRAには、ノードの設定値が反映されます。
+一部のみ指定することもできます - 例) `0.1::https://example.com/anylora.safetensors` (strength_modelが0.1になり、strength_clipとckpt_typeはノードの設定に従う) + +何のLoRAか忘れないようにコメントを書くこともできます。
+コメントは行頭を`#`にしてください。 + +例) +``` +#あのLoRA +https://example.com/anylora.safetensors + +#すごそうなLoRA +0.3::https://example.com/superlora.safetensors + +#いつものLoRA +0.5:0.8:file:favorite_lora_v6.safetensors +``` diff --git a/README.md b/README.md index 7b115de..d1d6ea5 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,45 @@ # ComfyUI-TemporaryLoader This is a custom node of ComfyUI that downloads and loads models from the input URL. The model is temporarily downloaded into memory and not saved to storage. -This could be useful when trying out models or when using various models on machines with limited storage. Since the model is downloaded into memory, expect higher memory usage than usual. +This could be useful when trying out models or when using various models on machines with limited storage. -## Installation: -1. Use `git clone https://github.com/pkpkTech/ComfyUI-TemporaryLoader` in your ComfyUI custom nodes directory +## Installation +1. Use `git clone https://github.com/pkpkTech/ComfyUI-TemporaryLoader` in your ComfyUI custom_nodes directory 1. Use `pip install -r requirements.txt` in ComfyUI-TemporaryLoader directory -## Usage +## Nodes The "Load Checkpoint (Temporary)" and "Load LoRA (Temporary)" nodes will be added to the temporary_loader category. In addition to the standard Load node, the following items are added - ckpt_url: URL of the model - ckpt_type: With `auto`, it looks at the file extension of the downloaded file. - download_split: Specify the number of splits for parallel downloading. + +In addition to these, a "Load Multi LoRA (Temporary)" node is also added.
+This is a node for loading multiple LoRAs.
+It can be loaded by URL or by specifying a filename in the same way as the standard LoRA Loader.
+Follow the format below + +`{LoRA URL}` or `file:{LoRA file name}` - e.g.) `https://example.com/anylora.safetensors` or `file:anylora.safetensors` + +`{strength_model}:{strength_clip}:{LoRA URL} or file:{LoRA file name}` - e.g.) `0.4:1.0:https://example.com/anylora.safetensors` + +`{strength_model}:{strength_clip}:{ckpt_type}:{LoRA URL} or file:{LoRA file name}` - e.g.) `0.4:1.0:other:https://example.com/anylora.pt` + +LoRAs for which strength_model, strength_clip and ckpt_type are not specified in text will reflect the node's set values. +You can also specify only a part - e.g.) `0.1::https://example.com/anylora.safetensors` (strength_model is 0.1, strength_clip and ckpt_type follow node settings). + +To avoid confusion as to what LoRA it is, you can also write a comment. +Comments should start the line with `#`. + +e.g.) +``` +#thatLoRA +https://example.com/anylora.safetensors + +#The LoRA of the super landscape +0.3::https://example.com/superlora.safetensors + +#The usual LoRA +0.5:0.8:file:favorite_lora_v6.safetensors +``` diff --git a/__init__.py b/__init__.py index 463e914..f1f5b7e 100644 --- a/__init__.py +++ b/__init__.py @@ -1,13 +1,16 @@ from .LoadTempCheckpoint import LoadTempCheckpoint from .LoadTempLoRA import LoadTempLoRA +from .LoadTempMultiLoRA import LoadTempMultiLoRA NODE_CLASS_MAPPINGS = { "LoadTempCheckpoint": LoadTempCheckpoint, - "LoadTempLoRA": LoadTempLoRA + "LoadTempLoRA": LoadTempLoRA, + "LoadTempMultiLoRA": LoadTempMultiLoRA, } NODE_DISPLAY_NAME_MAPPINGS = { - "LoadTempCheckpoint": "Load Checkpoint (Temporary)", - "LoadTempLoRA": "Load LoRA (Temporary)" + "LoadTempCheckpoint": "Checkpoint Loader (Temporary)", + "LoadTempLoRA": "Load LoRA (Temporary)", + "LoadTempMultiLoRA": "Load Multi LoRA (Temporary)", } __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']