Compare commits

...
11 Commits
Author SHA1 Message Date
Dijkstra a95cba4e3b doc: last fix 2024-04-09 22:18:14 +02:00
Dijkstra 40f09582e2 doc: added View info in readme 2024-04-09 22:17:08 +02:00
Dijkstra 6635d30566 doc: updated readme 2024-04-09 22:11:55 +02:00
dijkstra 5cf49c9565 Merge pull request #19 from idrirap/issue14
feat: added pyssss's lora view info
2024-04-09 21:24:05 +02:00
Dijkstra 1e8ed1e16f feat: added pyssss's lora view info 2024-04-09 21:09:44 +02:00
dijkstra 64113afd7f Merge pull request #18 from idrirap/issue16
feat: added lora name to output
2024-04-09 18:16:19 +02:00
Dijkstra 2bd6769b78 fix: now can use the output lora_name as an input for override_lora_name 2024-04-09 18:11:46 +02:00
Dijkstra 96a69f7cbe feat: added lora name to output 2024-04-09 17:05:22 +02:00
dijkstra 9d8b79844f Merge pull request #13 from idrirap/issue12
fix: broken preview when using both enable preview and override_lora_name
2024-03-14 23:26:17 +01:00
dijkstra d5641ba745 Merge pull request #11 from deipfei/json-error-change
don't delete loras_tags.json on JSONDecodeError
2024-03-08 21:21:03 +01:00
Dustin Pfeiffer 5b5eaf3a81 don't delete loras_tags.json on JSONDecodeError 2024-02-23 15:28:01 -08:00
12 changed files with 124 additions and 38 deletions
+70 -21
View File
@@ -2,37 +2,81 @@
This project is a fork of https://github.com/Extraltodeus/LoadLoraWithTags This project is a fork of https://github.com/Extraltodeus/LoadLoraWithTags
The aim of these custom nodes is to get an _easy_ access to the tags used to trigger a lora. The aim of these custom nodes is to get an _easy_ access to the tags used to trigger a lora.
This project is compatible with Stacked Loras from https://github.com/LucianoCirino/efficiency-nodes-comfyui/releases This project is compatible with Stacked Loras from https://github.com/jags111/efficiency-nodes-comfyui
I talk about **lora**, but works with **lycoris** too. When I talk about **lora**, I also mean **lycoris** too.
## Install ## Install
Some of this project nodes depends on https://github.com/pythongosssss/ComfyUI-Custom-Scripts : Some of this project nodes depends on https://github.com/pythongosssss/ComfyUI-Custom-Scripts :
- LoraLoaderAdvanced - LoraLoaderAdvanced
- LoraLoaderStackedAdvanced - LoraLoaderStackedAdvanced
They get their vanilla equivalents. - `View info...`
They get their vanilla equivalents with less features
Overall, Custom-Scripts is recommended to be able to know the content of the tag lists with the node `showText` Overall, Custom-Scripts is recommended to be able to know the content of the tag lists with the node `showText`
## Features ## Features
### Main nodes
#### Vanilla vs Advanced #### Vanilla vs Advanced
Vanilla refers to nodes that have no lora preview from the menu, nor the lora list. But the features provided are the same. > Vanilla refers to nodes that have no lora preview from the menu, nor the lora list. But the features provided are the same.
![image](./images/main.png) ![image](./images/main.png)
#### Nodes ### Nodes
- LoraLoader (Vanilla or Advanced) #### LoraLoader (Vanilla and Advanced)
- LoraLoaderStacked (Vanilla or Avanced). The stacked lora input is optional. ![image](./images/LoraLoader.png)
Allow to load a lora, either the normal way, or the efficiency-nodes way. INPUT
These loaders have two custom outputs: - `override_lora_name` (optional): Used to ignore the field `lora_name` and use the name passed. Should use [LoraListNames](#loralistnames) or the `lora_name` output.
- civitai_tags_list: a python list of the tags related to this lora on civitai
- meta_tags_list: a python list of the tags used for training the lora embeded in it (if any) FIELDS
This outputs needs to be filtered by two othere nodes: - `force_fetch`: Force the civitai fetching of data even if there is already something saved
- TagsFormater: Helper to show the available tag and their indexes - `enable_preview`: Toggle on/off the saved lora preview if any (only in advanced)
- tagsSelector: allow to filter tags and apply a weight to it. - `append_lora_if_empty`: Add the name of the lora to the list of tags if the list is empty
- TagSelector contains four parameters. First the selector (see Filtering next)
- The weight `(tag:weight)` OUTPUT
- The boolean `ensure_comma`. To properly append comma if a prefix or suffix is added. - `civitai_tags_list`: a python list of the tags related to this lora on civitai
- The boolean `append_loraname_if_empty` which will add the name of the lora in the list of outputs if they are empty. - `meta_tags_list`: a python list of the tags used for training the lora embeded in it (if any)
#### Filtering - `lora_name`: the name of the current selected lora
#### LoraLoaderStacked (Vanilla and Avanced).
![image](./images/LoraLoaderStacked.png)
INPUT
- `lora_stack` (optional): another stack of lora.
- `override_lora_name` (optional): Used to ignore the field `lora_name` and use the name passed. Should use [LoraListNames](#loralistnames) or the `lora_name` output.
FIELDS
- `force_fetch`: Force the civitai fetching of data even if there is already something saved
- `enable_preview`: Toggle on/off the saved lora preview if any (only in advanced)
- `append_lora_if_empty`: Add the name of the lora to the list of tags if the list is empty
OUTPUT
- `civitai_tags_list`: a python list of the tags related to this lora on civitai
- `meta_tags_list`: a python list of the tags used for training the lora embeded in it (if any)
- `lora_name`: the name of the current selected lora
#### LoraTagsOnly
![image](./images/LoraTagsOnly.png)
To get the tags without using the lora.
- `override_lora_name` (optional): Used to ignore the field `lora_name` and use the name passed. Should use [LoraListNames](#loralistnames) or the `lora_name` output.
OUTPUT
- `civitai_tags_list`: a python list of the tags related to this lora on civitai
- `meta_tags_list`: a python list of the tags used for training the lora embeded in it (if any)
- `lora_name`: the name of the current selected lora
#### TagsFormater
![image](./images/TagsFormater.png)
Helper to show the available tag and their indexes. Tags are sorted by training frequence. The more a tag was used, the higher in the list it is. Works for both `civitai_tags_list` and `meta_tags_list`
#### TagsSelector
![image](./images/TagsSelector.png)
Allow to filter tags and apply a weight to it.
TagSelector contains four parameters.
- `selector` (see the [Filtering](#filtering) section next)
- `weight`: to format the tag like `(tag:weight)`. Default set to 1 without the weight like `tag`.
- `ensure_comma`. To properly append comma if a prefix or suffix is added.
#### LoraListNames
![image](./images/LoraListNames.png)
List all the existing lora names. It is used as an input for `override_lora_name`
### Filtering
The format is simple. It's the same as python list index, but can select multiple index or ranges of indexes separated by comas. The format is simple. It's the same as python list index, but can select multiple index or ranges of indexes separated by comas.
`Ex: 0, 3, 5:8, -8:` `Ex: 0, 3, 5:8, -8:`
- Select a specific list of indexes: `0, 2, 3, 15`... - Select a specific list of indexes: `0, 2, 3, 15`...
@@ -42,6 +86,11 @@ The format is simple. It's the same as python list index, but can select multipl
- You can use negative indexes. Like `-1` to select the last tag - You can use negative indexes. Like `-1` to select the last tag
- By default `:` selects everything - By default `:` selects everything
### View Info
![image](./images/ViewInfo.png)
Pythongossss's [View Info...](https://github.com/pythongosssss/ComfyUI-Custom-Scripts?tab=readme-ov-file#checkpointloraembedding-info) feature from ComfyUI-Custom-Scripts
### Examples
#### Example of normal workflow #### Example of normal workflow
![image](./images/loaderAdvanced.png) ![image](./images/loaderAdvanced.png)
@@ -55,5 +104,5 @@ Lora Stack can also be chained together to load multiple loras into an efficient
### Side nodes I made and kept here ### Side nodes I made and kept here
- FusionText: takes two text input and join them together - FusionText: takes two text input and join them together
- Randomizer: takes two couples text+lorastack and return randomly one them - Randomizer: takes two couples text+lorastack and return randomly one of them
- TextInputBasic: just a text input with two additional input for text chaining - TextInputBasic: just a text input with two additional input for text chaining
+2 -1
View File
@@ -5,4 +5,5 @@ from .nodes_utils import NODE_CLASS_MAPPINGS as nu_NCM
NODE_CLASS_MAPPINGS = dict(na_NCM, **nu_NCM) NODE_CLASS_MAPPINGS = dict(na_NCM, **nu_NCM)
#NODE_DISPLAY_NAME_MAPPINGS = dict(na_NDNM, **nu_NDNM) #NODE_DISPLAY_NAME_MAPPINGS = dict(na_NDNM, **nu_NDNM)
__all__ = ["NODE_CLASS_MAPPINGS"]#, "NODE_DISPLAY_NAME_MAPPINGS"] WEB_DIRECTORY = "./web"
__all__ = ["NODE_CLASS_MAPPINGS", "WEB_DIRECTORY"]#, "NODE_DISPLAY_NAME_MAPPINGS"]
Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

+14 -14
View File
@@ -26,8 +26,8 @@ class LoraLoaderVanilla:
} }
} }
RETURN_TYPES = ("MODEL", "CLIP", "LIST", "LIST") RETURN_TYPES = ("MODEL", "CLIP", "LIST", "LIST", "STRING")
RETURN_NAMES = ("MODEL", "CLIP", "civitai_tags_list", "meta_tags_list") RETURN_NAMES = ("MODEL", "CLIP", "civitai_tags_list", "meta_tags_list", "lora_name")
FUNCTION = "load_lora" FUNCTION = "load_lora"
CATEGORY = "autotrigger" CATEGORY = "autotrigger"
@@ -56,7 +56,7 @@ class LoraLoaderVanilla:
model_lora, clip_lora = load_lora_for_models(model, clip, lora, strength_model, strength_clip) model_lora, clip_lora = load_lora_for_models(model, clip, lora, strength_model, strength_clip)
return (model_lora, clip_lora, civitai_tags_list, meta_tags_list) return (model_lora, clip_lora, civitai_tags_list, meta_tags_list, lora_name)
class LoraLoaderStackedVanilla: class LoraLoaderStackedVanilla:
@classmethod @classmethod
@@ -75,8 +75,8 @@ class LoraLoaderStackedVanilla:
} }
} }
RETURN_TYPES = ("LIST", "LIST", "LORA_STACK",) RETURN_TYPES = ("LIST", "LIST", "LORA_STACK", "STRING")
RETURN_NAMES = ("civitai_tags_list", "meta_tags_list", "LORA_STACK",) RETURN_NAMES = ("civitai_tags_list", "meta_tags_list", "LORA_STACK", "lora_name")
FUNCTION = "set_stack" FUNCTION = "set_stack"
#OUTPUT_NODE = False #OUTPUT_NODE = False
CATEGORY = "autotrigger" CATEGORY = "autotrigger"
@@ -96,7 +96,7 @@ class LoraLoaderStackedVanilla:
if lora_stack is not None: if lora_stack is not None:
loras.extend(lora_stack) loras.extend(lora_stack)
return (civitai_tags_list, meta_tags_list, loras) return (civitai_tags_list, meta_tags_list, loras, lora_name)
class LoraLoaderAdvanced: class LoraLoaderAdvanced:
def __init__(self): def __init__(self):
@@ -122,8 +122,8 @@ class LoraLoaderAdvanced:
} }
} }
RETURN_TYPES = ("MODEL", "CLIP", "LIST", "LIST") RETURN_TYPES = ("MODEL", "CLIP", "LIST", "LIST", "STRING")
RETURN_NAMES = ("MODEL", "CLIP", "civitai_tags_list", "meta_tags_list") RETURN_NAMES = ("MODEL", "CLIP", "civitai_tags_list", "meta_tags_list", "lora_name")
FUNCTION = "load_lora" FUNCTION = "load_lora"
CATEGORY = "autotrigger" CATEGORY = "autotrigger"
@@ -162,10 +162,10 @@ class LoraLoaderAdvanced:
"subfolder": "lora_preview", "subfolder": "lora_preview",
"type": "temp" "type": "temp"
} }
return {"ui": {"images": [preview_output]}, "result": (model_lora, clip_lora, civitai_tags_list, meta_tags_list)} return {"ui": {"images": [preview_output]}, "result": (model_lora, clip_lora, civitai_tags_list, meta_tags_list, lora_name["content"])}
return (model_lora, clip_lora, civitai_tags_list, meta_tags_list) return (model_lora, clip_lora, civitai_tags_list, meta_tags_list, lora_name["content"])
class LoraLoaderStackedAdvanced: class LoraLoaderStackedAdvanced:
@classmethod @classmethod
@@ -186,8 +186,8 @@ class LoraLoaderStackedAdvanced:
} }
} }
RETURN_TYPES = ("LIST", "LIST", "LORA_STACK",) RETURN_TYPES = ("LIST", "LIST", "LORA_STACK", "STRING")
RETURN_NAMES = ("civitai_tags_list", "meta_tags_list", "LORA_STACK",) RETURN_NAMES = ("civitai_tags_list", "meta_tags_list", "LORA_STACK", "lora_name")
FUNCTION = "set_stack" FUNCTION = "set_stack"
#OUTPUT_NODE = False #OUTPUT_NODE = False
CATEGORY = "autotrigger" CATEGORY = "autotrigger"
@@ -218,9 +218,9 @@ class LoraLoaderStackedAdvanced:
"subfolder": "lora_preview", "subfolder": "lora_preview",
"type": "temp" "type": "temp"
} }
return {"ui": {"images": [preview_output]}, "result": (civitai_tags_list, meta_tags_list, loras)} return {"ui": {"images": [preview_output]}, "result": (civitai_tags_list, meta_tags_list, loras, lora_name["content"])}
return {"result": (civitai_tags_list, meta_tags_list, loras)} return {"result": (civitai_tags_list, meta_tags_list, loras, lora_name["content"])}
class LoraTagsOnly: class LoraTagsOnly:
@classmethod @classmethod
+2 -2
View File
@@ -68,7 +68,7 @@ def load_json_from_file(file_path):
return None return None
except json.JSONDecodeError: except json.JSONDecodeError:
print(f"Error decoding JSON in file: {file_path}") print(f"Error decoding JSON in file: {file_path}")
return None raise
def save_dict_to_json(data_dict, file_path): def save_dict_to_json(data_dict, file_path):
try: try:
@@ -221,4 +221,4 @@ def append_lora_name_if_empty(tags_list, lora_path, enabled):
filename = os.path.basename(filename) filename = os.path.basename(filename)
tags_list.append(filename) tags_list.append(filename)
return tags_list return tags_list
+36
View File
@@ -0,0 +1,36 @@
import { app } from "../../../scripts/app.js";
import { LoraInfoDialog } from "../../ComfyUI-Custom-Scripts/js/modelInfo.js";
const infoHandlers = {
"LoraLoaderVanilla":true,
"LoraLoaderStackedVanilla":true,
"LoraLoaderAdvanced":true,
"LoraLoaderStackedAdvanced":true
}
app.registerExtension({
name: "autotrigger.LoraInfo",
beforeRegisterNodeDef(nodeType) {
if (! infoHandlers[nodeType.comfyClass]) {
return;
}
const getExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function (_, options) {
let value = this.widgets[0].value;
if (!value) {
return;
}
if (value.content) {
value = value.content;
}
options.unshift({
content: "View info...",
callback: async () => {
new LoraInfoDialog(value).show("loras", value);
},
});
return getExtraMenuOptions?.apply(this, arguments);
};
}
});