feat: add AODH Image Saver (Metadata), Lora Selector, Checkpoint Selector, and various node improvements
This commit is contained in:
77
README.md
77
README.md
@@ -12,8 +12,9 @@ The **Character JSON Reader** node allows you to load character definitions from
|
|||||||
- **Automatic File Discovery**: Scans the `nodes/character_reader/characters/` folder for `.json` files.
|
- **Automatic File Discovery**: Scans the `nodes/character_reader/characters/` folder for `.json` files.
|
||||||
- **Selection Modes**:
|
- **Selection Modes**:
|
||||||
- `manual`: Select a specific file from the dropdown.
|
- `manual`: Select a specific file from the dropdown.
|
||||||
- `sequential`: Cycles through files based on the `index` input.
|
- `sequential`: Cycles through files, starting from the selected file.
|
||||||
- `random`: Picks a random file using the `index` as a seed.
|
- `random`: Picks a random file. Uses a fresh random seed on every execution.
|
||||||
|
- **Repeat Count**: Keeps the same selection for a specified number of executions before changing.
|
||||||
- **Structured Outputs**: Provides 21 distinct output pins covering identity, wardrobe, and style.
|
- **Structured Outputs**: Provides 21 distinct output pins covering identity, wardrobe, and style.
|
||||||
- **Prompt-Ready Formatting**: Automatically appends a comma to every non-empty output string.
|
- **Prompt-Ready Formatting**: Automatically appends a comma to every non-empty output string.
|
||||||
- **Robustness**: Ensures all outputs are valid strings, even if fields are missing from the JSON.
|
- **Robustness**: Ensures all outputs are valid strings, even if fields are missing from the JSON.
|
||||||
@@ -24,11 +25,77 @@ The **Resolution Reader** node reads resolution configurations from text files.
|
|||||||
|
|
||||||
#### Features:
|
#### Features:
|
||||||
- **Selection Modes**:
|
- **Selection Modes**:
|
||||||
- `manual`: Uses the first line of the selected file.
|
- `manual`: Uses the selected line from the dropdown.
|
||||||
- `sequential`: Cycles through lines in the file based on the `index` input.
|
- `sequential`: Cycles through lines in the file, starting from the selected line.
|
||||||
- `random`: Picks a random line from the file using the `index` as a seed.
|
- `random`: Picks a random line from the file. Uses a fresh random seed on every execution.
|
||||||
|
- **Repeat Count**: Keeps the same selection for a specified number of executions before changing.
|
||||||
- **Outputs**: Provides `width` (INT), `height` (INT), and `upscale` (FLOAT).
|
- **Outputs**: Provides `width` (INT), `height` (INT), and `upscale` (FLOAT).
|
||||||
|
|
||||||
|
### Lora Selector
|
||||||
|
|
||||||
|
The **Lora Selector** node allows you to select a folder of Lora files (from `ComfyUI/models/Lora/Illustrious/`) and pick one `.safetensors` file at a time, either randomly or sequentially.
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
- **Folder Selection**: Scans `ComfyUI/models/Lora/Illustrious/` for subdirectories.
|
||||||
|
- **Selection Modes**:
|
||||||
|
- `Random`: Picks a random Lora from the folder. Uses a fresh random seed on every execution.
|
||||||
|
- `Sequential`: Cycles through the Lora files in the folder, starting from the `manual_index`.
|
||||||
|
- `Manual`: Selects the Lora at the specified `manual_index`.
|
||||||
|
- **Repeat Count**: Keeps the same selection for a specified number of executions before changing.
|
||||||
|
- **Manual Index**: Specifies the starting index for `Sequential` mode or the specific index for `Manual` mode.
|
||||||
|
- **Outputs**:
|
||||||
|
- `lora_name` (STRING): The relative path to the selected Lora.
|
||||||
|
- `total_loras` (INT): The total number of Lora files in the selected folder.
|
||||||
|
|
||||||
|
### Checkpoint Selector
|
||||||
|
|
||||||
|
The **Checkpoint Selector** node allows you to select a folder of Checkpoint files (from `ComfyUI/models/Stable-diffusion/`) and pick one `.safetensors` or `.ckpt` file at a time, either randomly or sequentially.
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
- **Folder Selection**: Scans `ComfyUI/models/Stable-diffusion/` (and other checkpoint paths) for subdirectories.
|
||||||
|
- **Selection Modes**:
|
||||||
|
- `Random`: Picks a random Checkpoint from the folder. Uses a fresh random seed on every execution.
|
||||||
|
- `Sequential`: Cycles through the Checkpoint files in the folder, starting from the `manual_index`.
|
||||||
|
- `Manual`: Selects the Checkpoint at the specified `manual_index`.
|
||||||
|
- **Repeat Count**: Keeps the same selection for a specified number of executions before changing.
|
||||||
|
- **Manual Index**: Specifies the starting index for `Sequential` mode or the specific index for `Manual` mode.
|
||||||
|
- **Outputs**:
|
||||||
|
- `checkpoint_name` (STRING): The relative path to the selected Checkpoint.
|
||||||
|
- `total_checkpoints` (INT): The total number of Checkpoint files in the selected folder.
|
||||||
|
|
||||||
|
### Checkpoint Loader (From String)
|
||||||
|
|
||||||
|
The **Checkpoint Loader (From String)** node loads a checkpoint model using a string input (e.g., from the **Checkpoint Selector** node).
|
||||||
|
|
||||||
|
#### Inputs:
|
||||||
|
- `ckpt_name` (STRING): The name/path of the checkpoint to load.
|
||||||
|
|
||||||
|
#### Outputs:
|
||||||
|
- `MODEL`: The loaded model.
|
||||||
|
- `CLIP`: The loaded CLIP model.
|
||||||
|
- `VAE`: The loaded VAE model.
|
||||||
|
|
||||||
|
### AODH Image Saver (Metadata)
|
||||||
|
|
||||||
|
The **AODH Image Saver (Metadata)** node saves images with comprehensive metadata in both A1111-compatible and extended ComfyUI formats.
|
||||||
|
|
||||||
|
#### Features:
|
||||||
|
- **Dynamic Directory Support**: Specify a `save_directory` with support for date formatting:
|
||||||
|
- `date:yyyy-MM-dd`: Replaces with formatted date (e.g., `2024-05-20`).
|
||||||
|
- Standard `strftime` patterns like `%Y-%m-%d`.
|
||||||
|
- **Comprehensive Metadata**: Embeds prompt, sampling parameters, model info, and LoRA details.
|
||||||
|
- **LoRA Handling**: Automatically strips directory paths from LoRA names for cleaner metadata.
|
||||||
|
- **Image Pass-through**: Includes an image output pin to continue the workflow after saving.
|
||||||
|
|
||||||
|
#### Inputs:
|
||||||
|
- `images` (IMAGE): The images to save.
|
||||||
|
- `filename_prefix` (STRING): The prefix for the saved files.
|
||||||
|
- `save_directory` (STRING): The folder to save images in (defaults to current date).
|
||||||
|
- Various optional metadata fields (prompts, checkpoint, lora, etc.).
|
||||||
|
|
||||||
|
#### Outputs:
|
||||||
|
- `images` (IMAGE): The input images passed through.
|
||||||
|
|
||||||
#### JSON Structure:
|
#### JSON Structure:
|
||||||
|
|
||||||
Place your character JSON files in the `nodes/character_reader/characters/` directory. The expected format is:
|
Place your character JSON files in the `nodes/character_reader/characters/` directory. The expected format is:
|
||||||
|
|||||||
35
character_ids.txt
Normal file
35
character_ids.txt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
aerith_gainsborough
|
||||||
|
android_18
|
||||||
|
anya_forger
|
||||||
|
bulma
|
||||||
|
camilla
|
||||||
|
cammy
|
||||||
|
chun_li
|
||||||
|
ciri
|
||||||
|
hatsune_miku
|
||||||
|
jessica_rabbit
|
||||||
|
jessie
|
||||||
|
jinx
|
||||||
|
k/da_all_out_ahri
|
||||||
|
k/da_all_out_akali
|
||||||
|
k/da_all_out_evelynn
|
||||||
|
k/da_all_out_kai'sa
|
||||||
|
komi_shouko
|
||||||
|
lara_croft_classic
|
||||||
|
lulu (ffx)
|
||||||
|
majin_android_21
|
||||||
|
marin_kitagawa
|
||||||
|
nessa
|
||||||
|
princess_peach
|
||||||
|
princess_zelda_botw
|
||||||
|
riju
|
||||||
|
rosalina
|
||||||
|
rouge_the_bat
|
||||||
|
ryouko_(tenchi_muyou!)
|
||||||
|
samus_aran
|
||||||
|
sucy_manbavaran
|
||||||
|
tifa_lockhart
|
||||||
|
urbosa
|
||||||
|
yor_briar
|
||||||
|
y'shtola_rhul
|
||||||
|
yuna_ffx
|
||||||
122
comfyui_metadata_spec.json
Normal file
122
comfyui_metadata_spec.json
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"_description": "Sample metadata structure for ComfyUI-generated images with A1111-compatible format.\n This format allows the app to parse ComfyUI images using the same parser as A1111 images,\n while preserving ComfyUI-specific metadata for advanced features.\n \n A ComfyUI node should embed this metadata in the 'parameters' PNG text chunk.",
|
||||||
|
|
||||||
|
"parameters": "masterpiece, best quality, amazing quality, very aesthetic, high resolution, ultra-detailed, 1girl, solo, looking at viewer, smile, long hair, blue eyes, school uniform\nNegative prompt: embedding:Illustrious/lazyneg_1760455, artist name, signature, watermark, blurry, low quality, bad anatomy, text, censored, deformed, bad hand\nSteps: 30, Sampler: euler_ancestral, CFG scale: 6.0, Seed: 927008042550191, Size: 1280x800, Model: illustrij_v20.safetensors, Model hash: a1b2c3d4e5f6, Clip skip: 2, RNG: GPU, Tiling: false, Restore faces: false, Hires upscale: 1.5, Hires steps: 15, Hires upscaler: RealESRGAN_x4plus_anime_6B, Denoising strength: 0.4",
|
||||||
|
|
||||||
|
"comfyui": true,
|
||||||
|
|
||||||
|
"comfyui_metadata": {
|
||||||
|
"_description": "Extended metadata specific to ComfyUI workflow",
|
||||||
|
|
||||||
|
"workflow_name": "Character Generator",
|
||||||
|
"workflow_version": "1.0.0",
|
||||||
|
|
||||||
|
"generation": {
|
||||||
|
"checkpoint": "Illustrious/illustrij_v20.safetensors",
|
||||||
|
"checkpoint_hash": "a1b2c3d4e5f6",
|
||||||
|
"vae": "sdxl_vae.safetensors",
|
||||||
|
"clip_skip": 2,
|
||||||
|
"lora": [
|
||||||
|
{
|
||||||
|
"name": "Illustrious/Styles/Noob/jyojifuku_noobai_V1.0.safetensors",
|
||||||
|
"strength_model": 1.0,
|
||||||
|
"strength_clip": 1.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"sampling": {
|
||||||
|
"sampler": "euler_ancestral",
|
||||||
|
"scheduler": "karras",
|
||||||
|
"steps": 30,
|
||||||
|
"cfg": 6.0,
|
||||||
|
"seed": 927008042550191,
|
||||||
|
"batch_size": 1
|
||||||
|
},
|
||||||
|
|
||||||
|
"resolution": {
|
||||||
|
"width": 1280,
|
||||||
|
"height": 800,
|
||||||
|
"upscale_factor": 1.5,
|
||||||
|
"upscaler": "RealESRGAN_x4plus_anime_6B",
|
||||||
|
"hires_steps": 15,
|
||||||
|
"denoise_strength": 0.4
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt_structure": {
|
||||||
|
"positive": {
|
||||||
|
"full": "masterpiece, best quality, amazing quality, very aesthetic, high resolution, ultra-detailed, 1girl, solo, looking at viewer, smile, long hair, blue eyes, school uniform",
|
||||||
|
"quality_tags": "masterpiece, best quality, amazing quality, very aesthetic, high resolution, ultra-detailed",
|
||||||
|
"character": {
|
||||||
|
"name": "sucy_manbavaran",
|
||||||
|
"description": "1girl, solo, looking at viewer, smile, long hair, blue eyes",
|
||||||
|
"outfit": "school uniform"
|
||||||
|
},
|
||||||
|
"style_tags": ""
|
||||||
|
},
|
||||||
|
"negative": {
|
||||||
|
"full": "embedding:Illustrious/lazyneg_1760455, artist name, signature, watermark, blurry, low quality, bad anatomy, text, censored, deformed, bad hand",
|
||||||
|
"embeddings": ["Illustrious/lazyneg_1760455"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"post_processing": {
|
||||||
|
"detailers": [
|
||||||
|
{
|
||||||
|
"type": "FaceDetailer",
|
||||||
|
"enabled": true,
|
||||||
|
"guide_size": 512,
|
||||||
|
"steps": 20,
|
||||||
|
"denoise": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "HandDetailer",
|
||||||
|
"enabled": true,
|
||||||
|
"guide_size": 512,
|
||||||
|
"steps": 24,
|
||||||
|
"denoise": 0.35
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"color_match": {
|
||||||
|
"enabled": true,
|
||||||
|
"color_space": "LAB",
|
||||||
|
"luminance_factor": 1.0,
|
||||||
|
"color_intensity_factor": 1.05
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"workflow": {
|
||||||
|
"nodes": {
|
||||||
|
"prompt_node_id": "374",
|
||||||
|
"prompt_node_type": "ShowText|pysssss",
|
||||||
|
"prompt_node_title": "Full Prompt",
|
||||||
|
"seed_node_id": "164",
|
||||||
|
"sampler_node_id": "169",
|
||||||
|
"checkpoint_loader_id": "332"
|
||||||
|
},
|
||||||
|
"groups": [
|
||||||
|
"Sampler",
|
||||||
|
"Character",
|
||||||
|
"FaceDetailer",
|
||||||
|
"HandDetailer",
|
||||||
|
"Upscaler"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"app_metadata": {
|
||||||
|
"_description": "Optional metadata for this app to enable advanced features",
|
||||||
|
|
||||||
|
"tags": ["1girl", "solo", "school uniform", "smile"],
|
||||||
|
"character": "sucy_manbavaran",
|
||||||
|
"rating": "safe",
|
||||||
|
"favorite": false,
|
||||||
|
"collections": ["Illustrious Characters"],
|
||||||
|
|
||||||
|
"custom_fields": {
|
||||||
|
"artist": "",
|
||||||
|
"source_url": "",
|
||||||
|
"notes": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
extract_character_ids.py
Normal file
44
extract_character_ids.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
def extract_ids():
|
||||||
|
characters_dir = 'nodes/character_reader/characters'
|
||||||
|
output_file = 'character_ids.txt'
|
||||||
|
|
||||||
|
if not os.path.exists(characters_dir):
|
||||||
|
print(f"Directory not found: {characters_dir}")
|
||||||
|
return
|
||||||
|
|
||||||
|
character_ids = []
|
||||||
|
|
||||||
|
# List all files in the directory
|
||||||
|
files = os.listdir(characters_dir)
|
||||||
|
# Sort files to ensure consistent output order
|
||||||
|
files.sort()
|
||||||
|
|
||||||
|
for filename in files:
|
||||||
|
if filename.endswith('.json'):
|
||||||
|
filepath = os.path.join(characters_dir, filename)
|
||||||
|
try:
|
||||||
|
with open(filepath, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
if 'character_id' in data:
|
||||||
|
character_ids.append(data['character_id'])
|
||||||
|
else:
|
||||||
|
print(f"Warning: 'character_id' not found in {filename}")
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
print(f"Error decoding JSON in {filename}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading {filename}: {e}")
|
||||||
|
|
||||||
|
# Write to output file
|
||||||
|
try:
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
for char_id in character_ids:
|
||||||
|
f.write(f"{char_id}\n")
|
||||||
|
print(f"Successfully wrote {len(character_ids)} character IDs to {output_file}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error writing to output file: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
extract_ids()
|
||||||
@@ -2,19 +2,31 @@ from .character_reader import NODE_CLASS_MAPPINGS as CR_CLASS, NODE_DISPLAY_NAME
|
|||||||
from .resolution_reader import NODE_CLASS_MAPPINGS as RR_CLASS, NODE_DISPLAY_NAME_MAPPINGS as RR_DISPLAY
|
from .resolution_reader import NODE_CLASS_MAPPINGS as RR_CLASS, NODE_DISPLAY_NAME_MAPPINGS as RR_DISPLAY
|
||||||
from .reenforcer import NODE_CLASS_MAPPINGS as RE_CLASS, NODE_DISPLAY_NAME_MAPPINGS as RE_DISPLAY
|
from .reenforcer import NODE_CLASS_MAPPINGS as RE_CLASS, NODE_DISPLAY_NAME_MAPPINGS as RE_DISPLAY
|
||||||
from .lora_from_string import NODE_CLASS_MAPPINGS as LFS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as LFS_DISPLAY
|
from .lora_from_string import NODE_CLASS_MAPPINGS as LFS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as LFS_DISPLAY
|
||||||
|
from .lora_selector import NODE_CLASS_MAPPINGS as LS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as LS_DISPLAY
|
||||||
|
from .checkpoint_selector import NODE_CLASS_MAPPINGS as CS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as CS_DISPLAY
|
||||||
|
from .checkpoint_from_string import NODE_CLASS_MAPPINGS as CFS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as CFS_DISPLAY
|
||||||
|
from .metadata_saver import NODE_CLASS_MAPPINGS as MS_CLASS, NODE_DISPLAY_NAME_MAPPINGS as MS_DISPLAY
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
**CR_CLASS,
|
**CR_CLASS,
|
||||||
**RR_CLASS,
|
**RR_CLASS,
|
||||||
**RE_CLASS,
|
**RE_CLASS,
|
||||||
**LFS_CLASS
|
**LFS_CLASS,
|
||||||
|
**LS_CLASS,
|
||||||
|
**CS_CLASS,
|
||||||
|
**CFS_CLASS,
|
||||||
|
**MS_CLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
**CR_DISPLAY,
|
**CR_DISPLAY,
|
||||||
**RR_DISPLAY,
|
**RR_DISPLAY,
|
||||||
**RE_DISPLAY,
|
**RE_DISPLAY,
|
||||||
**LFS_DISPLAY
|
**LFS_DISPLAY,
|
||||||
|
**LS_DISPLAY,
|
||||||
|
**CS_DISPLAY,
|
||||||
|
**CFS_DISPLAY,
|
||||||
|
**MS_DISPLAY
|
||||||
}
|
}
|
||||||
|
|
||||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||||
|
|||||||
@@ -30,45 +30,59 @@ class CharacterJsonReader:
|
|||||||
"STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING",
|
"STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING", "STRING",
|
||||||
"STRING", "STRING", "STRING", "STRING", "STRING", "STRING",
|
"STRING", "STRING", "STRING", "STRING", "STRING", "STRING",
|
||||||
"STRING", "STRING", "STRING", "STRING",
|
"STRING", "STRING", "STRING", "STRING",
|
||||||
"STRING", "FLOAT", "STRING"
|
"STRING", "FLOAT", "STRING", "INT"
|
||||||
)
|
)
|
||||||
RETURN_NAMES = (
|
RETURN_NAMES = (
|
||||||
"name", "base_specs", "hair", "eyes", "expression", "hands", "arms", "torso", "pelvis", "legs", "feet", "distinguishing_marks",
|
"name", "base_specs", "hair", "eyes", "expression", "hands", "arms", "torso", "pelvis", "legs", "feet", "distinguishing_marks",
|
||||||
"inner_layer", "outer_layer", "lower_body", "footwear", "gloves", "accessories",
|
"inner_layer", "outer_layer", "lower_body", "footwear", "gloves", "accessories",
|
||||||
"aesthetic", "primary_color", "secondary_color", "tertiary_color",
|
"aesthetic", "primary_color", "secondary_color", "tertiary_color",
|
||||||
"lora_name", "lora_weight", "lora_triggers"
|
"lora_name", "lora_weight", "lora_triggers", "total_characters"
|
||||||
)
|
)
|
||||||
|
|
||||||
FUNCTION = "read_character"
|
FUNCTION = "read_character"
|
||||||
CATEGORY = "AODH Pack"
|
CATEGORY = "AODH Pack"
|
||||||
|
|
||||||
_current_index = 0
|
def __init__(self):
|
||||||
_current_count = 0
|
self.current_index = 0
|
||||||
_last_selection = None
|
self.current_count = 0
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_start_file = None
|
||||||
|
|
||||||
def read_character(self, character_file, selection_mode, repeat_count):
|
def read_character(self, character_file, selection_mode, repeat_count):
|
||||||
|
repeat_count = max(1, repeat_count)
|
||||||
base_path = os.path.dirname(os.path.realpath(__file__))
|
base_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
char_dir = os.path.join(base_path, "characters")
|
char_dir = os.path.join(base_path, "characters")
|
||||||
|
|
||||||
characters = sorted([f for f in os.listdir(char_dir) if f.endswith('.json')])
|
characters = sorted([f for f in os.listdir(char_dir) if f.endswith('.json')])
|
||||||
|
|
||||||
if not characters:
|
if not characters:
|
||||||
return ("",) * 22 + ("", 0.0, "")
|
return ("",) * 22 + ("", 0.0, "", 0)
|
||||||
|
|
||||||
|
# Reset sequence if character_file changes
|
||||||
|
if self.last_start_file != character_file:
|
||||||
|
try:
|
||||||
|
self.current_index = characters.index(character_file)
|
||||||
|
except ValueError:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_start_file = character_file
|
||||||
|
|
||||||
if selection_mode == "manual":
|
if selection_mode == "manual":
|
||||||
selected_file = character_file
|
selected_file = character_file
|
||||||
elif selection_mode == "sequential":
|
elif selection_mode == "sequential":
|
||||||
if self.__class__._current_count >= repeat_count:
|
if self.current_count >= repeat_count:
|
||||||
self.__class__._current_index += 1
|
self.current_index += 1
|
||||||
self.__class__._current_count = 0
|
self.current_count = 0
|
||||||
selected_file = characters[self.__class__._current_index % len(characters)]
|
selected_file = characters[self.current_index % len(characters)]
|
||||||
self.__class__._current_count += 1
|
self.current_count += 1
|
||||||
elif selection_mode == "random":
|
elif selection_mode == "random":
|
||||||
if self.__class__._current_count >= repeat_count or self.__class__._last_selection is None or self.__class__._last_selection not in characters:
|
if self.current_count >= repeat_count or self.last_selection is None or self.last_selection not in characters:
|
||||||
self.__class__._last_selection = random.choice(characters)
|
# Use a local Random instance to ensure randomness regardless of global seed
|
||||||
self.__class__._current_count = 0
|
rng = random.Random()
|
||||||
selected_file = self.__class__._last_selection
|
self.last_selection = rng.choice(characters)
|
||||||
self.__class__._current_count += 1
|
self.current_count = 0
|
||||||
|
selected_file = self.last_selection
|
||||||
|
self.current_count += 1
|
||||||
else:
|
else:
|
||||||
selected_file = character_file
|
selected_file = character_file
|
||||||
|
|
||||||
@@ -114,4 +128,5 @@ class CharacterJsonReader:
|
|||||||
get_val(lora, "lora_name", prepend_comma=False),
|
get_val(lora, "lora_name", prepend_comma=False),
|
||||||
float(lora.get("lora_weight", 0.0) if lora.get("lora_weight") else 0.0),
|
float(lora.get("lora_weight", 0.0) if lora.get("lora_weight") else 0.0),
|
||||||
get_val(lora, "lora_triggers"),
|
get_val(lora, "lora_triggers"),
|
||||||
|
len(characters),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "black short-sleeved shirt",
|
"inner_layer": "black short-sleeved shirt",
|
||||||
"outer_layer": "blue denim vest, Red Ribbon logo on back",
|
"outer_layer": "blue denim vest, 'RR' text on back",
|
||||||
"lower_body": "blue denim skirt, black leggings",
|
"lower_body": "blue denim skirt, black leggings",
|
||||||
"footwear": "brown boots",
|
"footwear": "brown boots",
|
||||||
"gloves": "",
|
"gloves": "",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"character_id": "anya_forger",
|
"character_id": "anya_(spy_x_family)",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, small build, fair skin",
|
"base_specs": "1girl, small build, fair skin",
|
||||||
"hair": "short pink hair, two small horns (hair ornaments)",
|
"hair": "short pink hair, two small horns (hair ornaments)",
|
||||||
|
|||||||
35
nodes/character_reader/characters/biwa_hayahide.json
Normal file
35
nodes/character_reader/characters/biwa_hayahide.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "biwa_hayahide_(Umamusume)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, horse ears, horse tail, tall",
|
||||||
|
"hair": "long grey hair, wild hair",
|
||||||
|
"eyes": "purple eyes, red framed glasses",
|
||||||
|
"expression": "thinking",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "large breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt",
|
||||||
|
"outer_layer": "tracen school uniform",
|
||||||
|
"lower_body": "pleated skirt",
|
||||||
|
"footwear": "heeled shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": ""
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "intellectual, cool",
|
||||||
|
"primary_color": "maroon",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "grey"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "pink dress, 'Bulma' text on front",
|
"outer_layer": "black playboy bunny",
|
||||||
"lower_body": "dress",
|
"lower_body": "pantyhose",
|
||||||
"footwear": "purple sneakers, white socks",
|
"footwear": "red high heels",
|
||||||
"gloves": "purple gloves",
|
"gloves": "detatched cuffs",
|
||||||
"accessories": "red hair ribbon, dragon radar"
|
"accessories": "red hair ribbon, dragon radar"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"character_id": "camilla",
|
"character_id": "camilla_(fire_emblem)",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, curvaceous build, fair skin",
|
"base_specs": "1girl, curvaceous build, fair skin",
|
||||||
"hair": "long wavy lavender hair, hair covering one eye",
|
"hair": "long wavy lavender hair, hair covering one eye",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"character_id": "chun_li",
|
"character_id": "chun_li",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, muscular build, fair skin",
|
"base_specs": "1girl, muscular build, fair skin, asian",
|
||||||
"hair": "black hair, hair buns",
|
"hair": "black hair, hair buns",
|
||||||
"eyes": "brown eyes",
|
"eyes": "brown eyes",
|
||||||
"expression": "determined smile",
|
"expression": "determined smile",
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
"accessories": "white hair ribbons, spiked bracelets"
|
"accessories": "white hair ribbons, spiked bracelets"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "martial arts, traditional, street fighter style",
|
"aesthetic": "chinese style",
|
||||||
"primary_color": "blue",
|
"primary_color": "blue",
|
||||||
"secondary_color": "white",
|
"secondary_color": "white",
|
||||||
"tertiary_color": "gold"
|
"tertiary_color": "gold"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"character_id": "ciri",
|
"character_id": "ciri",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, athletic build, pale skin",
|
"base_specs": "1girl, athletic build",
|
||||||
"hair": "ashen grey hair, messy bun",
|
"hair": "ashen grey hair, messy bun",
|
||||||
"eyes": "emerald green eyes. mascara",
|
"eyes": "emerald green eyes, mascara",
|
||||||
"expression": "determined look",
|
"expression": "determined look",
|
||||||
"hands": "brown nails",
|
"hands": "green nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "medium breasts",
|
"torso": "medium breasts",
|
||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "white blouse",
|
"inner_layer": "white blouse",
|
||||||
"outer_layer": "brown leather vest, silver studs",
|
"outer_layer": "",
|
||||||
"lower_body": "brown leather trousers",
|
"lower_body": "brown leather trousers",
|
||||||
"footwear": "brown leather boots",
|
"footwear": "brown leather boots",
|
||||||
"gloves": "brown leather gloves",
|
"gloves": "brown leather gloves",
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "delinquent_mother_flim13",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, milf, gyaru, tall",
|
||||||
|
"hair": "blonde hair, long hair",
|
||||||
|
"eyes": "sharp eyes",
|
||||||
|
"expression": "smirk, sharp teeth",
|
||||||
|
"hands": "painted nails",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "very large breasts",
|
||||||
|
"pelvis": "wide hips",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "biege sweater, cleavage",
|
||||||
|
"outer_layer": "",
|
||||||
|
"lower_body": "pencil skirt",
|
||||||
|
"footwear": "high heels",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "necklace, rings"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "gyaru, milf, pink leopard print",
|
||||||
|
"primary_color": "pink",
|
||||||
|
"secondary_color": "black",
|
||||||
|
"tertiary_color": "gold"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "Illustrious/Looks/Gyaru_mom_Flim13_IL_V1.safetensors",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/gold_city.json
Normal file
35
nodes/character_reader/characters/gold_city.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "gold_city_(Umamusume)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, horse ears, horse tail, tall",
|
||||||
|
"hair": "blonde hair, wavy hair",
|
||||||
|
"eyes": "blue eyes",
|
||||||
|
"expression": "confident expression",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt",
|
||||||
|
"outer_layer": "tracen school uniform",
|
||||||
|
"lower_body": "pleated skirt",
|
||||||
|
"footwear": "heeled shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "choker, earrings"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "fashionable, model",
|
||||||
|
"primary_color": "gold",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "black"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/gold_ship.json
Normal file
35
nodes/character_reader/characters/gold_ship.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "gold_ship_(Umamusume)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, horse ears, horse tail, tall",
|
||||||
|
"hair": "grey hair, short hair",
|
||||||
|
"eyes": "red eyes",
|
||||||
|
"expression": "crazy expression, grin",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt",
|
||||||
|
"outer_layer": "tracen school uniform",
|
||||||
|
"lower_body": "pleated skirt",
|
||||||
|
"footwear": "heeled shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "ear covers, hat"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "energetic, sporty",
|
||||||
|
"primary_color": "red",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "gold"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/jessica_rabbit.json
Normal file
35
nodes/character_reader/characters/jessica_rabbit.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "jessica_rabbit",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, voluptuous build, tall,",
|
||||||
|
"hair": "long red hair, side part, hair over one eye",
|
||||||
|
"eyes": "green eyes, heavy makeup, purple eyeshadow",
|
||||||
|
"expression": "seductive smile",
|
||||||
|
"hands": "purple elbow gloves",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "large breasts",
|
||||||
|
"pelvis": "narrow waist",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": "red lips"
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "",
|
||||||
|
"outer_layer": "red sequin dress, strapless, high slit, backless",
|
||||||
|
"lower_body": "side_slit,",
|
||||||
|
"footwear": "red high heels",
|
||||||
|
"gloves": "purple opera gloves",
|
||||||
|
"accessories": "gold earrings, glitter"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "noir, cartoon, glamorous",
|
||||||
|
"primary_color": "red",
|
||||||
|
"secondary_color": "purple",
|
||||||
|
"tertiary_color": "gold"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 0.8,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
"outer_layer": "white Team Rocket uniform jacket, bare stomach, red R logo",
|
"outer_layer": "white Team Rocket uniform jacket, bare stomach, red R logo",
|
||||||
"lower_body": "white miniskirt",
|
"lower_body": "white miniskirt",
|
||||||
"footwear": "black thigh-high boots",
|
"footwear": "black thigh-high boots",
|
||||||
"gloves": "black gloves",
|
"gloves": "black elbow gloves",
|
||||||
"accessories": ""
|
"accessories": "green earrings"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "villainous, anime, pokemon style",
|
"aesthetic": "villainous, anime, pokemon style",
|
||||||
|
|||||||
35
nodes/character_reader/characters/jinx.json
Normal file
35
nodes/character_reader/characters/jinx.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "jinx",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, slender build, pale skin,",
|
||||||
|
"hair": "long aqua hair, twin braids, very long hair, bangs",
|
||||||
|
"eyes": "pink eyes, ",
|
||||||
|
"expression": "crazy eyes, manic grin, crazy smile",
|
||||||
|
"hands": "black and pink nails",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "flat chest,",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": "cloud tattoo,"
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "",
|
||||||
|
"outer_layer": "pink and black bikini, asymmetrical_bikini ",
|
||||||
|
"lower_body": "pink shorts, single pink stocking",
|
||||||
|
"footwear": "combat boots",
|
||||||
|
"gloves": "black fingerless gloves, fishnet elbow gloves,",
|
||||||
|
"accessories": "ammo belts, choker, bullet necklace,"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "punk, chaotic,",
|
||||||
|
"primary_color": "pink",
|
||||||
|
"secondary_color": "black",
|
||||||
|
"tertiary_color": "aqua"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "Illustrious/Looks/jinx_default_lol-000021.safetensors",
|
||||||
|
"lora_weight": 0.8,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/kagamine_rin.json
Normal file
35
nodes/character_reader/characters/kagamine_rin.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "kagamine_rin",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, petite",
|
||||||
|
"hair": "blonde hair, short hair, hair bow",
|
||||||
|
"eyes": "blue eyes",
|
||||||
|
"expression": "smile, energetic",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "detached sleeves",
|
||||||
|
"torso": "flat chest",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "leg warmers",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt, sailor collar",
|
||||||
|
"outer_layer": "",
|
||||||
|
"lower_body": "black shorts, yellow belt",
|
||||||
|
"footwear": "white shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "headset, hair bow"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "vocaloid, cyber",
|
||||||
|
"primary_color": "yellow",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "black"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"lower_body": "black leather shorts",
|
"lower_body": "black leather shorts",
|
||||||
"footwear": "black thigh-high boots",
|
"footwear": "black thigh-high boots",
|
||||||
"gloves": "",
|
"gloves": "",
|
||||||
"accessories": "crystal orb, silver jewelry"
|
"accessories": "crystal heart, silver jewelry"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "pop star, mystical, k/da style",
|
"aesthetic": "pop star, mystical, k/da style",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
"hair": "light blue hair,",
|
"hair": "light blue hair,",
|
||||||
"eyes": "yellow glowing eyes, slit pupils",
|
"eyes": "yellow glowing eyes, slit pupils",
|
||||||
"expression": "seductive, confident look",
|
"expression": "seductive, confident look",
|
||||||
"hands": "long black claws, blue nails",
|
"hands": "metal claws",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "medium breasts",
|
"torso": "medium breasts",
|
||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
"legs": "",
|
"legs": "",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
"distinguishing_marks": "purple markings under eyes, floating crystal cannons"
|
"distinguishing_marks": ""
|
||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "silver bodysuit",
|
"inner_layer": "silver bodysuit",
|
||||||
|
|||||||
35
nodes/character_reader/characters/komi_shouko.json
Normal file
35
nodes/character_reader/characters/komi_shouko.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "komi_shouko",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, slender build, pale skin, asian",
|
||||||
|
"hair": "long dark purple hair, hime cut,",
|
||||||
|
"eyes": "dark purple eyes,",
|
||||||
|
"expression": "neutral expression, stoic, cat ears",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "black pantyhose",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt",
|
||||||
|
"outer_layer": "itan private high school uniform, blazer, striped bow tie",
|
||||||
|
"lower_body": "plaid skirt",
|
||||||
|
"footwear": "loafers",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": ""
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "anime, manga, clean lines",
|
||||||
|
"primary_color": "purple",
|
||||||
|
"secondary_color": "magenta",
|
||||||
|
"tertiary_color": "white"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 0.8,
|
||||||
|
"lora_triggers": "komi shouko, itan private high school uniform"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"character_id": "lara_croft_classic",
|
"character_id": "lara_croft_classic",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, athletic build, tan skin",
|
"base_specs": "1girl, athletic build,",
|
||||||
"hair": "long brown hair, single braid",
|
"hair": "long brown hair, single braid",
|
||||||
"eyes": "brown eyes",
|
"eyes": "brown eyes",
|
||||||
"expression": "determined",
|
"expression": "light smile, raised eyebrow",
|
||||||
"hands": "",
|
"hands": "",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "",
|
"torso": "large breasts",
|
||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
"legs": "",
|
"legs": "",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "teal tank top, crop top",
|
"outer_layer": "teal tank top,",
|
||||||
"lower_body": "brown shorts",
|
"lower_body": "brown shorts",
|
||||||
"footwear": "brown combat boots, red laces",
|
"footwear": "brown combat boots, red laces",
|
||||||
"gloves": "black fingerless gloves",
|
"gloves": "black fingerless gloves",
|
||||||
"accessories": "dual thigh holsters, backpack, circular sunglasses"
|
"accessories": "dual thigh pistol holsters, brown leatherbackpack, red circular sunglasses"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "adventure, retro, 90s style",
|
"aesthetic": "adventure, retro, 90s style",
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
"tertiary_color": "black"
|
"tertiary_color": "black"
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"lora_name": "lara_croft_classic",
|
"lora_name": "Illustrious/Looks/LaraCroft_ClassicV2_Illu_Dwnsty.safetensors",
|
||||||
"lora_weight": 0.8,
|
"lora_weight": 0.8,
|
||||||
"lora_triggers": "lara croft, classic outfit"
|
"lora_triggers": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
35
nodes/character_reader/characters/lisa_minci.json
Normal file
35
nodes/character_reader/characters/lisa_minci.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "lisa_(genshin_impact)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, tall, mature female",
|
||||||
|
"hair": "brown hair, wavy hair, side ponytail",
|
||||||
|
"eyes": "green eyes",
|
||||||
|
"expression": "seductive smile",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "detached sleeves",
|
||||||
|
"torso": "large breasts",
|
||||||
|
"pelvis": "wide hips",
|
||||||
|
"legs": "black pantyhose",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": "beauty mark"
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "purple dress, corset",
|
||||||
|
"outer_layer": "purple shawl",
|
||||||
|
"lower_body": "slit skirt",
|
||||||
|
"footwear": "black heels",
|
||||||
|
"gloves": "purple gloves",
|
||||||
|
"accessories": "witch hat, rose, necklace"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "genshin impact, witch, librarian",
|
||||||
|
"primary_color": "purple",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "gold"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"character_id": "lulu (ffx)",
|
"character_id": "lulu (ff10)",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, curvaceous build, fair skin",
|
"base_specs": "1girl, curvaceous build, fair skin",
|
||||||
"hair": "long black hair, complex braids, hairpins",
|
"hair": "long black hair, complex braids, hairpins",
|
||||||
"eyes": "red eyes",
|
"eyes": "red eyes",
|
||||||
"expression": "stern expression",
|
"expression": "thinking, raised eyebrow",
|
||||||
"hands": "black nails",
|
"hands": "black nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "large breasts",
|
"torso": "large breasts",
|
||||||
|
|||||||
@@ -4,22 +4,22 @@
|
|||||||
"base_specs": "1girl, curvaceous build, pink skin",
|
"base_specs": "1girl, curvaceous build, pink skin",
|
||||||
"hair": "long voluminous white hair",
|
"hair": "long voluminous white hair",
|
||||||
"eyes": "red eyes, black sclera",
|
"eyes": "red eyes, black sclera",
|
||||||
"expression": "playful yet menacing smile",
|
"expression": "evil smile",
|
||||||
"hands": "black claws, pink nails",
|
"hands": "black claws, pink nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "medium breasts",
|
"torso": "large breasts",
|
||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
"legs": "",
|
"legs": "",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
"distinguishing_marks": "pink skin, long tail, pointed ears"
|
"distinguishing_marks": "pink skin, long tail, pointy ears"
|
||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "black tube top",
|
"inner_layer": "black tube top",
|
||||||
"outer_layer": "",
|
"outer_layer": "",
|
||||||
"lower_body": "white baggy pants",
|
"lower_body": "white harem pants",
|
||||||
"footwear": "black and yellow boots",
|
"footwear": "black and yellow boots",
|
||||||
"gloves": "",
|
"gloves": "black sleeves",
|
||||||
"accessories": "gold bracelets, gold neck ring"
|
"accessories": "gold bracelets, gold neck ring, hoop earrings"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "supernatural, anime, dragon ball style",
|
"aesthetic": "supernatural, anime, dragon ball style",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"character_id": "marin_kitagawa",
|
"character_id": "marin_kitagawa",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, slender build, fair skin",
|
"base_specs": "1girl, slender build, fair skin, asian",
|
||||||
"hair": "long blonde hair, pink tips",
|
"hair": "long blonde hair, pink tips",
|
||||||
"eyes": "pink eyes (contacts)",
|
"eyes": "pink eyes (contacts)",
|
||||||
"expression": "excited smile",
|
"expression": "excited smile",
|
||||||
|
|||||||
35
nodes/character_reader/characters/megurine_luka.json
Normal file
35
nodes/character_reader/characters/megurine_luka.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "megurine_luka",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, tall, mature female",
|
||||||
|
"hair": "pink hair, long hair",
|
||||||
|
"eyes": "blue eyes",
|
||||||
|
"expression": "light smile",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "",
|
||||||
|
"outer_layer": "crop top, detached sleeves, gold trim",
|
||||||
|
"lower_body": "side slit, lace-up skirt",
|
||||||
|
"footwear": "thinghighs, lace-up boots, gold boots, gold armlet",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "headset"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "vocaloid, elegant",
|
||||||
|
"primary_color": "black",
|
||||||
|
"secondary_color": "gold",
|
||||||
|
"tertiary_color": "pink"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/meiko.json
Normal file
35
nodes/character_reader/characters/meiko.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "meiko",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, mature female",
|
||||||
|
"hair": "brown hair, short hair",
|
||||||
|
"eyes": "brown eyes",
|
||||||
|
"expression": "smile, confident",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "red crop top, sleeveless",
|
||||||
|
"outer_layer": "",
|
||||||
|
"lower_body": "red skirt, mini skirt",
|
||||||
|
"footwear": "brown boots",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "choker"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "vocaloid, casual",
|
||||||
|
"primary_color": "red",
|
||||||
|
"secondary_color": "brown",
|
||||||
|
"tertiary_color": "black"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "olivier_mira_armstrong",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, tall, mature female",
|
||||||
|
"hair": "blonde hair, long hair, hair over one eye",
|
||||||
|
"eyes": "blue eyes, sharp eyes",
|
||||||
|
"expression": "serious",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": "thick lips"
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "black shirt",
|
||||||
|
"outer_layer": "blue military coat, fur collar",
|
||||||
|
"lower_body": "black pants",
|
||||||
|
"footwear": "black boots",
|
||||||
|
"gloves": "black gloves",
|
||||||
|
"accessories": "sword"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "military, amestris uniform",
|
||||||
|
"primary_color": "blue",
|
||||||
|
"secondary_color": "black",
|
||||||
|
"tertiary_color": "gold"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"hair": "long blonde hair, voluminous, crown",
|
"hair": "long blonde hair, voluminous, crown",
|
||||||
"eyes": "blue eyes, long eyelashes",
|
"eyes": "blue eyes, long eyelashes",
|
||||||
"expression": "gentle smile",
|
"expression": "gentle smile",
|
||||||
"hands": "pink nails",
|
"hands": "",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "medium breasts",
|
"torso": "medium breasts",
|
||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"base_specs": "1girl, slender build, fair skin, pointed ears",
|
"base_specs": "1girl, slender build, fair skin, pointed ears",
|
||||||
"hair": "long blonde hair, braided, gold hair clips",
|
"hair": "long blonde hair, braided, gold hair clips",
|
||||||
"eyes": "green eyes",
|
"eyes": "green eyes",
|
||||||
"expression": "determined look",
|
"expression": "curious",
|
||||||
"hands": "gold nails",
|
"hands": "gold nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "small breasts",
|
"torso": "small breasts",
|
||||||
|
|||||||
35
nodes/character_reader/characters/rice_shower.json
Normal file
35
nodes/character_reader/characters/rice_shower.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "rice_shower_(Umamusume)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, petite, horse ears, horse tail",
|
||||||
|
"hair": "long dark brown hair, bangs, hair over one eye",
|
||||||
|
"eyes": "purple eyes",
|
||||||
|
"expression": "shy expression",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "small breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "white shirt",
|
||||||
|
"outer_layer": "tracen school uniform",
|
||||||
|
"lower_body": "pleated skirt",
|
||||||
|
"footwear": "heeled shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "blue rose, hair flower, small hat, dagger"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "gothic lolita, elegant",
|
||||||
|
"primary_color": "purple",
|
||||||
|
"secondary_color": "blue",
|
||||||
|
"tertiary_color": "black"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,25 +11,25 @@
|
|||||||
"pelvis": "",
|
"pelvis": "",
|
||||||
"legs": "",
|
"legs": "",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
"distinguishing_marks": "blue lipstick,gerudo markings"
|
"distinguishing_marks": "darkblue lipstick,"
|
||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "gerudo top, colorful sash",
|
"outer_layer": "black top, blue sash",
|
||||||
"lower_body": "gerudo pants, puffy pants",
|
"lower_body": "black skirt, pelvic curtain,",
|
||||||
"footwear": "sandals",
|
"footwear": "gold high heels",
|
||||||
"gloves": "",
|
"gloves": "",
|
||||||
"accessories": "gold jewelry, crown, earrings"
|
"accessories": "gold jewelry, earrings"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "fantasy, desert, gerudo style",
|
"aesthetic": "fantasy, desert, gerudo style",
|
||||||
"primary_color": "gold",
|
"primary_color": "gold",
|
||||||
"secondary_color": "teal",
|
"secondary_color": "black",
|
||||||
"tertiary_color": "red"
|
"tertiary_color": "red"
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"lora_name": "riju_botw",
|
"lora_name": "",
|
||||||
"lora_weight": 0.8,
|
"lora_weight": 0.8,
|
||||||
"lora_triggers": "riju, gerudo"
|
"lora_triggers": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,21 +15,21 @@
|
|||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "black skin-tight jumpsuit, pink heart-shaped chest plate",
|
"outer_layer": "black skin-tight jumpsuit, pink heart-shaped chest plate, bare shoulders, cleavage",
|
||||||
"lower_body": "jumpsuit",
|
"lower_body": "jumpsuit",
|
||||||
"footwear": "white boots, pink heart motifs",
|
"footwear": "white boots, pink heart motifs",
|
||||||
"gloves": "white gloves, pink cuffs",
|
"gloves": "white gloves, pink cuffs",
|
||||||
"accessories": "blue eyeshadow"
|
"accessories": "blue eyeshadow"
|
||||||
},
|
},
|
||||||
"styles": {
|
"styles": {
|
||||||
"aesthetic": "sleek, spy, sonic style",
|
"aesthetic": "jewels, museum,sleek, spy, sonic style",
|
||||||
"primary_color": "white",
|
"primary_color": "white",
|
||||||
"secondary_color": "pink",
|
"secondary_color": "pink",
|
||||||
"tertiary_color": "black"
|
"tertiary_color": "black"
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"lora_name": "",
|
"lora_name": "Illustrious/Looks/Rouge_the_bat_v2.safetensors",
|
||||||
"lora_weight": 1.0,
|
"lora_weight": 0.8,
|
||||||
"lora_triggers": ""
|
"lora_triggers": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
35
nodes/character_reader/characters/ryouko_hakubi.json
Normal file
35
nodes/character_reader/characters/ryouko_hakubi.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "ryouko_(tenchi_muyou!)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, slim build,",
|
||||||
|
"hair": "long teal hair, spiky, voluminous",
|
||||||
|
"eyes": "golden eyes, cat-like pupils",
|
||||||
|
"expression": "confident smirk",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "medium breasts",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": "red gem on forehead,"
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "long white dress, plunging neckline, black belt",
|
||||||
|
"outer_layer": "black and orange long sleeve jacket with purple trim,",
|
||||||
|
"lower_body": "side_slit,, red trousers",
|
||||||
|
"footwear": "",
|
||||||
|
"gloves": "red gloves",
|
||||||
|
"accessories": "red gems, wristbands"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "90s anime, sci-fi",
|
||||||
|
"primary_color": "teal",
|
||||||
|
"secondary_color": "white",
|
||||||
|
"tertiary_color": "red"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 0.8,
|
||||||
|
"lora_triggers": "ryouko hakubi, space pirate"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/sarah_miller.json
Normal file
35
nodes/character_reader/characters/sarah_miller.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "sarah_miller_(the_last_of_us)",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, loli, small build",
|
||||||
|
"hair": "blonde hair, short hair",
|
||||||
|
"eyes": "blue eyes",
|
||||||
|
"expression": "smile",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "",
|
||||||
|
"torso": "flat chest",
|
||||||
|
"pelvis": "",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "grey t-shirt, white shirt",
|
||||||
|
"outer_layer": "",
|
||||||
|
"lower_body": "blue jeans",
|
||||||
|
"footwear": "sneakers",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "wristwatch"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "casual, 2013 fashion",
|
||||||
|
"primary_color": "grey",
|
||||||
|
"secondary_color": "blue",
|
||||||
|
"tertiary_color": "white"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
35
nodes/character_reader/characters/shantae.json
Normal file
35
nodes/character_reader/characters/shantae.json
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"character_id": "shantae",
|
||||||
|
"identity": {
|
||||||
|
"base_specs": "1girl, dark skin, pointy ears",
|
||||||
|
"hair": "purple hair, very long hair, ponytail",
|
||||||
|
"eyes": "blue eyes",
|
||||||
|
"expression": "smile, energetic",
|
||||||
|
"hands": "",
|
||||||
|
"arms": "gold bracelets",
|
||||||
|
"torso": "small breasts, perky breasts",
|
||||||
|
"pelvis": "wide hips",
|
||||||
|
"legs": "",
|
||||||
|
"feet": "",
|
||||||
|
"distinguishing_marks": ""
|
||||||
|
},
|
||||||
|
"wardrobe": {
|
||||||
|
"inner_layer": "",
|
||||||
|
"outer_layer": "red bikini top, red harem pants, gold trim",
|
||||||
|
"lower_body": "",
|
||||||
|
"footwear": "gold shoes",
|
||||||
|
"gloves": "",
|
||||||
|
"accessories": "gold tiara, hoop earrings"
|
||||||
|
},
|
||||||
|
"styles": {
|
||||||
|
"aesthetic": "genie, dancer, arabian",
|
||||||
|
"primary_color": "red",
|
||||||
|
"secondary_color": "gold",
|
||||||
|
"tertiary_color": "purple"
|
||||||
|
},
|
||||||
|
"lora": {
|
||||||
|
"lora_name": "",
|
||||||
|
"lora_weight": 1.0,
|
||||||
|
"lora_triggers": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,13 @@
|
|||||||
"character_id": "sucy_manbavaran",
|
"character_id": "sucy_manbavaran",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, lanky build, pale skin",
|
"base_specs": "1girl, lanky build, pale skin",
|
||||||
"hair": "mauve hair, hair covering one eye",
|
"hair": "light purple hair, hair covering one eye",
|
||||||
"eyes": "droopy red eyes",
|
"eyes": "red eyes",
|
||||||
"expression": "deadpan expression",
|
"expression": "deadpan expression",
|
||||||
"hands": "purple nails",
|
"hands": "black nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
"torso": "small breasts",
|
"torso": "small breasts",
|
||||||
"pelvis": "",
|
"pelvis": "narrow waist",
|
||||||
"legs": "",
|
"legs": "",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
"distinguishing_marks": "dark circles under eyes"
|
"distinguishing_marks": "dark circles under eyes"
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "dark purple witch robes",
|
"outer_layer": "dark purple witch robes",
|
||||||
"lower_body": "long skirt",
|
"lower_body": "long skirt with frayed edges",
|
||||||
"footwear": "brown boots",
|
"footwear": "brown boots",
|
||||||
"gloves": "",
|
"gloves": "",
|
||||||
"accessories": "pointed witch hat, potion bottle"
|
"accessories": "pointed witch hat, potion bottle"
|
||||||
|
|||||||
@@ -7,16 +7,16 @@
|
|||||||
"expression": "confident",
|
"expression": "confident",
|
||||||
"hands": "gold nails",
|
"hands": "gold nails",
|
||||||
"arms": "muscular arms",
|
"arms": "muscular arms",
|
||||||
"torso": "abs",
|
"torso": "abs, mediumS breasts",
|
||||||
"pelvis": "wide hips",
|
"pelvis": "wide hips",
|
||||||
"legs": "muscular legs",
|
"legs": "muscular legs",
|
||||||
"feet": "",
|
"feet": "",
|
||||||
"distinguishing_marks": "blue lipstick, gerudo markings"
|
"distinguishing_marks": "dark blue lipstick, gerudo markings"
|
||||||
},
|
},
|
||||||
"wardrobe": {
|
"wardrobe": {
|
||||||
"inner_layer": "",
|
"inner_layer": "",
|
||||||
"outer_layer": "gold breastplate, blue champion's skirt",
|
"outer_layer": "blue top, blue champion's skirt, green sash, green shoulder guards,",
|
||||||
"lower_body": "",
|
"lower_body": "blue skirt",
|
||||||
"footwear": "gold heels",
|
"footwear": "gold heels",
|
||||||
"gloves": "",
|
"gloves": "",
|
||||||
"accessories": "gold jewelry, scimitar"
|
"accessories": "gold jewelry, scimitar"
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
"tertiary_color": "red"
|
"tertiary_color": "red"
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"lora_name": "urbosa_botw",
|
"lora_name": "",
|
||||||
"lora_weight": 0.8,
|
"lora_weight": 0.8,
|
||||||
"lora_triggers": "urbosa, gerudo"
|
"lora_triggers": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, miqo'te, slender build, fair skin, cat ears",
|
"base_specs": "1girl, miqo'te, slender build, fair skin, cat ears",
|
||||||
"hair": "short white hair, bangs",
|
"hair": "short white hair, bangs",
|
||||||
"eyes": "blind white eyes",
|
"eyes": "blind, white eyes",
|
||||||
"expression": "stoic expression",
|
"expression": "stoic expression",
|
||||||
"hands": "black nails",
|
"hands": "black nails",
|
||||||
"arms": "",
|
"arms": "",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"character_id": "yuna_ffx",
|
"character_id": "yuna_(ff10)",
|
||||||
"identity": {
|
"identity": {
|
||||||
"base_specs": "1girl, slender, fair skin",
|
"base_specs": "1girl, slender, fair skin",
|
||||||
"hair": "short brown hair, bob cut",
|
"hair": "short brown hair, bob cut",
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
"tertiary_color": "yellow"
|
"tertiary_color": "yellow"
|
||||||
},
|
},
|
||||||
"lora": {
|
"lora": {
|
||||||
"lora_name": "yuna_ffx",
|
"lora_name": "",
|
||||||
"lora_weight": 0.8,
|
"lora_weight": 0.8,
|
||||||
"lora_triggers": "yuna, summoner outfit"
|
"lora_triggers": ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
9
nodes/checkpoint_from_string/__init__.py
Normal file
9
nodes/checkpoint_from_string/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from .checkpoint_from_string import CheckpointLoaderFromString
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"CheckpointLoaderFromString": CheckpointLoaderFromString
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"CheckpointLoaderFromString": "Checkpoint Loader (From String)"
|
||||||
|
}
|
||||||
19
nodes/checkpoint_from_string/checkpoint_from_string.py
Normal file
19
nodes/checkpoint_from_string/checkpoint_from_string.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import folder_paths
|
||||||
|
import comfy.sd
|
||||||
|
|
||||||
|
class CheckpointLoaderFromString:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"ckpt_name": ("STRING", {"multiline": False, "forceInput": True}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RETURN_TYPES = ("MODEL", "CLIP", "VAE")
|
||||||
|
FUNCTION = "load_checkpoint"
|
||||||
|
CATEGORY = "AODH Pack"
|
||||||
|
|
||||||
|
def load_checkpoint(self, ckpt_name):
|
||||||
|
ckpt_path = folder_paths.get_full_path_or_raise("checkpoints", ckpt_name)
|
||||||
|
out = comfy.sd.load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings"))
|
||||||
|
return out[:3]
|
||||||
9
nodes/checkpoint_selector/__init__.py
Normal file
9
nodes/checkpoint_selector/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from .checkpoint_selector import CheckpointSelector
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"CheckpointSelector": CheckpointSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"CheckpointSelector": "Checkpoint Selector"
|
||||||
|
}
|
||||||
120
nodes/checkpoint_selector/checkpoint_selector.py
Normal file
120
nodes/checkpoint_selector/checkpoint_selector.py
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
import folder_paths
|
||||||
|
|
||||||
|
class CheckpointSelector:
|
||||||
|
def __init__(self):
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_folder = None
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_manual_index = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
# Find folders in 'checkpoints' paths
|
||||||
|
checkpoint_paths = folder_paths.get_folder_paths("checkpoints")
|
||||||
|
checkpoint_folders = []
|
||||||
|
|
||||||
|
for path in checkpoint_paths:
|
||||||
|
if os.path.exists(path) and os.path.isdir(path):
|
||||||
|
# List subdirectories
|
||||||
|
try:
|
||||||
|
subdirs = [d for d in os.listdir(path) if os.path.isdir(os.path.join(path, d))]
|
||||||
|
checkpoint_folders.extend(subdirs)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Remove duplicates and sort
|
||||||
|
checkpoint_folders = sorted(list(set(checkpoint_folders)))
|
||||||
|
|
||||||
|
if not checkpoint_folders:
|
||||||
|
checkpoint_folders = ["None"]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"folder": (checkpoint_folders, ),
|
||||||
|
"mode": (["Random", "Sequential", "Manual"], {"default": "Random"}),
|
||||||
|
"repeat_count": ("INT", {"default": 1, "min": 1, "max": 100, "step": 1}),
|
||||||
|
"manual_index": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING", "INT")
|
||||||
|
RETURN_NAMES = ("checkpoint_name", "total_checkpoints")
|
||||||
|
FUNCTION = "select_checkpoint"
|
||||||
|
CATEGORY = "AODH Pack"
|
||||||
|
|
||||||
|
def select_checkpoint(self, folder, mode, repeat_count, manual_index):
|
||||||
|
repeat_count = max(1, repeat_count)
|
||||||
|
if folder == "None":
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
# Find the full path for the selected folder
|
||||||
|
checkpoint_paths = folder_paths.get_folder_paths("checkpoints")
|
||||||
|
target_path = None
|
||||||
|
|
||||||
|
for path in checkpoint_paths:
|
||||||
|
check_path = os.path.join(path, folder)
|
||||||
|
if os.path.exists(check_path) and os.path.isdir(check_path):
|
||||||
|
target_path = check_path
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_path:
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
# Find checkpoint files (.safetensors, .ckpt)
|
||||||
|
extensions = ('.safetensors', '.ckpt')
|
||||||
|
checkpoint_files = [f for f in os.listdir(target_path) if f.lower().endswith(extensions)]
|
||||||
|
checkpoint_files.sort()
|
||||||
|
|
||||||
|
count = len(checkpoint_files)
|
||||||
|
if count == 0:
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
selected_checkpoint = ""
|
||||||
|
|
||||||
|
# Reset state if folder changes
|
||||||
|
if self.last_folder != folder:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_folder = folder
|
||||||
|
|
||||||
|
# Reset state if manual_index changes (for Sequential mode)
|
||||||
|
if self.last_manual_index != manual_index:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_manual_index = manual_index
|
||||||
|
|
||||||
|
if mode == "Random":
|
||||||
|
if self.current_count >= repeat_count or self.last_selection is None:
|
||||||
|
rng = random.Random()
|
||||||
|
self.last_selection = rng.choice(checkpoint_files)
|
||||||
|
self.current_count = 0
|
||||||
|
|
||||||
|
selected_checkpoint = self.last_selection
|
||||||
|
self.current_count += 1
|
||||||
|
|
||||||
|
elif mode == "Sequential":
|
||||||
|
if self.current_count >= repeat_count:
|
||||||
|
self.current_index += 1
|
||||||
|
self.current_count = 0
|
||||||
|
|
||||||
|
selected_checkpoint = checkpoint_files[(self.current_index + manual_index) % count]
|
||||||
|
self.current_count += 1
|
||||||
|
|
||||||
|
elif mode == "Manual":
|
||||||
|
selected_checkpoint = checkpoint_files[manual_index % count]
|
||||||
|
|
||||||
|
# Construct the relative path for ComfyUI Checkpoint loader
|
||||||
|
# Use forward slashes for consistency with ComfyUI paths
|
||||||
|
full_checkpoint_name = f"{folder}/{selected_checkpoint}"
|
||||||
|
|
||||||
|
return (full_checkpoint_name, count)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def IS_CHANGED(s, folder, mode, repeat_count, manual_index):
|
||||||
|
if mode == "Random" or mode == "Sequential":
|
||||||
|
return float("nan")
|
||||||
|
return f"{folder}_{manual_index}"
|
||||||
9
nodes/lora_selector/__init__.py
Normal file
9
nodes/lora_selector/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from .lora_selector import LoraSelector
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"LoraSelector": LoraSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"LoraSelector": "Lora Selector"
|
||||||
|
}
|
||||||
121
nodes/lora_selector/lora_selector.py
Normal file
121
nodes/lora_selector/lora_selector.py
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import os
|
||||||
|
import random
|
||||||
|
import folder_paths
|
||||||
|
|
||||||
|
class LoraSelector:
|
||||||
|
def __init__(self):
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_folder = None
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_manual_index = 0
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
# Find 'Illustrious' folders in all lora paths
|
||||||
|
lora_paths = folder_paths.get_folder_paths("loras")
|
||||||
|
illustrious_folders = []
|
||||||
|
|
||||||
|
for path in lora_paths:
|
||||||
|
illustrious_path = os.path.join(path, "Illustrious")
|
||||||
|
if os.path.exists(illustrious_path) and os.path.isdir(illustrious_path):
|
||||||
|
# List subdirectories
|
||||||
|
try:
|
||||||
|
subdirs = [d for d in os.listdir(illustrious_path) if os.path.isdir(os.path.join(illustrious_path, d))]
|
||||||
|
illustrious_folders.extend(subdirs)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Remove duplicates and sort
|
||||||
|
illustrious_folders = sorted(list(set(illustrious_folders)))
|
||||||
|
|
||||||
|
if not illustrious_folders:
|
||||||
|
illustrious_folders = ["None"]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"folder": (illustrious_folders, ),
|
||||||
|
"mode": (["Random", "Sequential", "Manual"], {"default": "Random"}),
|
||||||
|
"repeat_count": ("INT", {"default": 1, "min": 1, "max": 100, "step": 1}),
|
||||||
|
"manual_index": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING", "INT")
|
||||||
|
RETURN_NAMES = ("lora_name", "total_loras")
|
||||||
|
FUNCTION = "select_lora"
|
||||||
|
CATEGORY = "AODH Pack"
|
||||||
|
|
||||||
|
def select_lora(self, folder, mode, repeat_count, manual_index):
|
||||||
|
repeat_count = max(1, repeat_count)
|
||||||
|
if folder == "None":
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
# Find the full path for the selected folder
|
||||||
|
lora_paths = folder_paths.get_folder_paths("loras")
|
||||||
|
target_path = None
|
||||||
|
|
||||||
|
for path in lora_paths:
|
||||||
|
check_path = os.path.join(path, "Illustrious", folder)
|
||||||
|
if os.path.exists(check_path) and os.path.isdir(check_path):
|
||||||
|
target_path = check_path
|
||||||
|
break
|
||||||
|
|
||||||
|
if not target_path:
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
# Find .safetensors files
|
||||||
|
lora_files = [f for f in os.listdir(target_path) if f.endswith('.safetensors')]
|
||||||
|
lora_files.sort() # Ensure consistent order
|
||||||
|
|
||||||
|
count = len(lora_files)
|
||||||
|
if count == 0:
|
||||||
|
return ("", 0)
|
||||||
|
|
||||||
|
selected_lora = ""
|
||||||
|
|
||||||
|
# Reset state if folder changes
|
||||||
|
if self.last_folder != folder:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_folder = folder
|
||||||
|
|
||||||
|
# Reset state if manual_index changes (for Sequential mode)
|
||||||
|
if self.last_manual_index != manual_index:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_manual_index = manual_index
|
||||||
|
|
||||||
|
if mode == "Random":
|
||||||
|
if self.current_count >= repeat_count or self.last_selection is None:
|
||||||
|
# Use a local Random instance to ensure randomness regardless of global seed
|
||||||
|
rng = random.Random()
|
||||||
|
self.last_selection = rng.choice(lora_files)
|
||||||
|
self.current_count = 0
|
||||||
|
|
||||||
|
selected_lora = self.last_selection
|
||||||
|
self.current_count += 1
|
||||||
|
|
||||||
|
elif mode == "Sequential":
|
||||||
|
if self.current_count >= repeat_count:
|
||||||
|
self.current_index += 1
|
||||||
|
self.current_count = 0
|
||||||
|
|
||||||
|
selected_lora = lora_files[(self.current_index + manual_index) % count]
|
||||||
|
self.current_count += 1
|
||||||
|
|
||||||
|
elif mode == "Manual":
|
||||||
|
selected_lora = lora_files[manual_index % count]
|
||||||
|
|
||||||
|
# Construct the relative path for ComfyUI Lora loader
|
||||||
|
# Use forward slashes for consistency
|
||||||
|
full_lora_name = f"Illustrious/{folder}/{selected_lora}"
|
||||||
|
|
||||||
|
return (full_lora_name, count)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def IS_CHANGED(s, folder, mode, repeat_count, manual_index):
|
||||||
|
if mode == "Random" or mode == "Sequential":
|
||||||
|
return float("nan")
|
||||||
|
return f"{folder}_{manual_index}"
|
||||||
9
nodes/metadata_saver/__init__.py
Normal file
9
nodes/metadata_saver/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from .metadata_saver import AodhMetadataImageSaver
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"AodhMetadataImageSaver": AodhMetadataImageSaver
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"AodhMetadataImageSaver": "AODH Image Saver (Metadata)"
|
||||||
|
}
|
||||||
210
nodes/metadata_saver/metadata_saver.py
Normal file
210
nodes/metadata_saver/metadata_saver.py
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import numpy as np
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
from PIL import Image
|
||||||
|
from PIL.PngImagePlugin import PngInfo
|
||||||
|
import folder_paths
|
||||||
|
import comfy.samplers
|
||||||
|
|
||||||
|
class AodhMetadataImageSaver:
|
||||||
|
def __init__(self):
|
||||||
|
self.output_dir = folder_paths.get_output_directory()
|
||||||
|
self.type = "output"
|
||||||
|
self.prefix_append = ""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"images": ("IMAGE", ),
|
||||||
|
"filename_prefix": ("STRING", {"default": "ComfyUI"}),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"save_directory": ("STRING", {"default": "%Y-%m-%d"}),
|
||||||
|
# Generation parameters
|
||||||
|
"positive_prompt": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"negative_prompt": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"checkpoint_name": ("STRING", {"default": ""}),
|
||||||
|
"vae_name": ("STRING", {"default": ""}),
|
||||||
|
"clip_skip": ("INT", {"default": -2}),
|
||||||
|
"lora_name": ("STRING", {"default": ""}),
|
||||||
|
"lora_strength": ("FLOAT", {"default": 1.0}),
|
||||||
|
|
||||||
|
# Sampling
|
||||||
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||||
|
"steps": ("INT", {"default": 20}),
|
||||||
|
"cfg": ("FLOAT", {"default": 8.0}),
|
||||||
|
"sampler_name": (comfy.samplers.KSampler.SAMPLERS, ),
|
||||||
|
"scheduler": (comfy.samplers.KSampler.SCHEDULERS, ),
|
||||||
|
|
||||||
|
# Resolution
|
||||||
|
"width": ("INT", {"default": 512}),
|
||||||
|
"height": ("INT", {"default": 512}),
|
||||||
|
"upscale_factor": ("FLOAT", {"default": 1.0}),
|
||||||
|
"upscaler_name": ("STRING", {"default": ""}),
|
||||||
|
"hires_steps": ("INT", {"default": 10}),
|
||||||
|
"denoise": ("FLOAT", {"default": 0.0}),
|
||||||
|
|
||||||
|
# Character / App Metadata
|
||||||
|
"character_name": ("STRING", {"default": ""}),
|
||||||
|
"tags": ("STRING", {"default": ""}),
|
||||||
|
"rating": (["safe", "questionable", "explicit"], {"default": "safe"}),
|
||||||
|
|
||||||
|
# Extension
|
||||||
|
"extension": (["png", "jpg", "webp"], {"default": "png"}),
|
||||||
|
"include_workflow": ("BOOLEAN", {"default": True}),
|
||||||
|
},
|
||||||
|
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
RETURN_NAMES = ("images",)
|
||||||
|
FUNCTION = "save_images"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
CATEGORY = "AODH Pack/Image"
|
||||||
|
|
||||||
|
def save_images(self, images, filename_prefix="ComfyUI", save_directory="%Y-%m-%d",
|
||||||
|
positive_prompt="", negative_prompt="",
|
||||||
|
checkpoint_name="", vae_name="", clip_skip=-2, lora_name="", lora_strength=1.0,
|
||||||
|
seed=0, steps=20, cfg=8.0, sampler_name="euler", scheduler="normal",
|
||||||
|
width=512, height=512, upscale_factor=1.0, upscaler_name="", hires_steps=10, denoise=0.0,
|
||||||
|
character_name="", tags="", rating="safe", extension="png", include_workflow=True,
|
||||||
|
prompt=None, extra_pnginfo=None):
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
if save_directory:
|
||||||
|
# Handle date:yyyy-MM-dd format
|
||||||
|
if "date:" in save_directory:
|
||||||
|
def replace_date(match):
|
||||||
|
fmt = match.group(1)
|
||||||
|
# Convert common JS-like date formats to python strftime
|
||||||
|
fmt = fmt.replace("yyyy", "%Y").replace("MM", "%m").replace("dd", "%d")
|
||||||
|
fmt = fmt.replace("HH", "%H").replace("mm", "%M").replace("ss", "%S")
|
||||||
|
return now.strftime(fmt)
|
||||||
|
save_directory = re.sub(r"date:([\w\-\:]+)", replace_date, save_directory)
|
||||||
|
|
||||||
|
# Also handle direct strftime patterns if any % is present
|
||||||
|
if "%" in save_directory:
|
||||||
|
save_directory = now.strftime(save_directory)
|
||||||
|
|
||||||
|
filename_prefix = os.path.join(save_directory, filename_prefix)
|
||||||
|
|
||||||
|
full_output_folder, filename, counter, subfolder, filename_prefix = \
|
||||||
|
folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||||
|
|
||||||
|
results = list()
|
||||||
|
for image in images:
|
||||||
|
i = 255. * image.cpu().numpy()
|
||||||
|
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||||
|
metadata = PngInfo()
|
||||||
|
|
||||||
|
# 1. Construct A1111 Parameters String
|
||||||
|
# Format: Prompt
|
||||||
|
# Negative prompt: ...
|
||||||
|
# Steps: ..., Sampler: ..., CFG scale: ..., Seed: ..., Size: ...x..., Model: ..., ...
|
||||||
|
|
||||||
|
parameters_text = f"{positive_prompt}\nNegative prompt: {negative_prompt}\n"
|
||||||
|
parameters_text += f"Steps: {steps}, Sampler: {sampler_name}, Schedule: {scheduler}, CFG scale: {cfg}, Seed: {seed}, "
|
||||||
|
parameters_text += f"Size: {width}x{height}, Model: {checkpoint_name}"
|
||||||
|
|
||||||
|
if clip_skip != -2:
|
||||||
|
parameters_text += f", Clip skip: {clip_skip}"
|
||||||
|
|
||||||
|
if upscale_factor > 1.0:
|
||||||
|
parameters_text += f", Hires upscale: {upscale_factor}, Hires steps: {hires_steps}, Hires upscaler: {upscaler_name}, Denoising strength: {denoise}"
|
||||||
|
|
||||||
|
if lora_name:
|
||||||
|
display_lora_name = Path(lora_name).name
|
||||||
|
parameters_text += f", Lora: {display_lora_name}"
|
||||||
|
|
||||||
|
metadata.add_text("parameters", parameters_text)
|
||||||
|
|
||||||
|
# 2. Construct ComfyUI Metadata JSON (following spec)
|
||||||
|
comfy_meta = {
|
||||||
|
"_description": "Extended metadata specific to ComfyUI workflow",
|
||||||
|
"workflow_name": "AODH Generator", # Could be dynamic if we parsed extra_pnginfo
|
||||||
|
"workflow_version": "1.0.0",
|
||||||
|
"generation": {
|
||||||
|
"checkpoint": checkpoint_name,
|
||||||
|
"vae": vae_name,
|
||||||
|
"clip_skip": clip_skip,
|
||||||
|
"lora": [{"name": Path(lora_name).name, "strength_model": lora_strength}] if lora_name else []
|
||||||
|
},
|
||||||
|
"sampling": {
|
||||||
|
"sampler": sampler_name,
|
||||||
|
"scheduler": scheduler,
|
||||||
|
"steps": steps,
|
||||||
|
"cfg": cfg,
|
||||||
|
"seed": seed,
|
||||||
|
"batch_size": images.shape[0]
|
||||||
|
},
|
||||||
|
"resolution": {
|
||||||
|
"width": width,
|
||||||
|
"height": height,
|
||||||
|
"upscale_factor": upscale_factor,
|
||||||
|
"upscaler": upscaler_name,
|
||||||
|
"hires_steps": hires_steps,
|
||||||
|
"denoise_strength": denoise
|
||||||
|
},
|
||||||
|
"prompt_structure": {
|
||||||
|
"positive": {"full": positive_prompt},
|
||||||
|
"negative": {"full": negative_prompt}
|
||||||
|
},
|
||||||
|
"workflow": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Embed raw workflow if available
|
||||||
|
if include_workflow:
|
||||||
|
if prompt is not None:
|
||||||
|
comfy_meta["workflow"]["execution"] = prompt
|
||||||
|
if extra_pnginfo is not None and "workflow" in extra_pnginfo:
|
||||||
|
comfy_meta["workflow"]["nodes"] = extra_pnginfo["workflow"]
|
||||||
|
|
||||||
|
# 3. App Metadata
|
||||||
|
app_meta = {
|
||||||
|
"tags": [t.strip() for t in tags.split(",") if t.strip()],
|
||||||
|
"character": character_name,
|
||||||
|
"rating": rating
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add to main JSON
|
||||||
|
full_metadata = {
|
||||||
|
"parameters": parameters_text,
|
||||||
|
"source": "comfyui",
|
||||||
|
"comfyui": True,
|
||||||
|
"comfyui_metadata": comfy_meta,
|
||||||
|
"app_metadata": app_meta
|
||||||
|
}
|
||||||
|
|
||||||
|
# Embed as "comment" or a specific key.
|
||||||
|
# The spec says "A ComfyUI node should embed this metadata in the 'parameters' PNG text chunk."
|
||||||
|
# But usually JSON goes into a separate chunk or we rely on the parser to extract it from 'parameters' if it was formatted there.
|
||||||
|
# However, standard ComfyUI puts workflow in "workflow" and "prompt" chunks.
|
||||||
|
# The spec implies a specific JSON structure. We'll add it as a separate text chunk "comfyui_metadata_json"
|
||||||
|
# OR we can try to append it to parameters, but A1111 parsers might break.
|
||||||
|
# Let's put the full JSON in a "aodh_metadata" chunk for safety,
|
||||||
|
# and ALSO try to adhere to standard Comfy behavior by keeping workflow/prompt chunks.
|
||||||
|
|
||||||
|
if include_workflow:
|
||||||
|
if prompt is not None:
|
||||||
|
metadata.add_text("prompt", json.dumps(prompt))
|
||||||
|
if extra_pnginfo is not None:
|
||||||
|
for x in extra_pnginfo:
|
||||||
|
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||||
|
|
||||||
|
# Add our custom full spec JSON
|
||||||
|
metadata.add_text("aodh_metadata", json.dumps(full_metadata, indent=2))
|
||||||
|
|
||||||
|
file = f"{filename}_{counter:05}_.{extension}"
|
||||||
|
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4)
|
||||||
|
results.append({
|
||||||
|
"filename": file,
|
||||||
|
"subfolder": subfolder,
|
||||||
|
"type": self.type
|
||||||
|
})
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
return { "ui": { "images": results }, "result": (images,) }
|
||||||
@@ -29,17 +29,20 @@ class ResolutionReader:
|
|||||||
return float("nan")
|
return float("nan")
|
||||||
return resolution
|
return resolution
|
||||||
|
|
||||||
RETURN_TYPES = ("INT", "INT", "FLOAT")
|
RETURN_TYPES = ("INT", "INT", "FLOAT", "INT")
|
||||||
RETURN_NAMES = ("width", "height", "upscale")
|
RETURN_NAMES = ("width", "height", "upscale", "total_resolutions")
|
||||||
|
|
||||||
FUNCTION = "read_resolution"
|
FUNCTION = "read_resolution"
|
||||||
CATEGORY = "AODH Pack"
|
CATEGORY = "AODH Pack"
|
||||||
|
|
||||||
_current_index = 0
|
def __init__(self):
|
||||||
_current_count = 0
|
self.current_index = 0
|
||||||
_last_selection = None
|
self.current_count = 0
|
||||||
|
self.last_selection = None
|
||||||
|
self.last_start_resolution = None
|
||||||
|
|
||||||
def read_resolution(self, resolution, selection_mode, repeat_count):
|
def read_resolution(self, resolution, selection_mode, repeat_count):
|
||||||
|
repeat_count = max(1, repeat_count)
|
||||||
base_path = os.path.dirname(os.path.realpath(__file__))
|
base_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
file_path = os.path.join(base_path, "resolutions", "resolutions.txt")
|
file_path = os.path.join(base_path, "resolutions", "resolutions.txt")
|
||||||
|
|
||||||
@@ -49,22 +52,33 @@ class ResolutionReader:
|
|||||||
lines = [line.strip() for line in f.readlines() if line.strip()]
|
lines = [line.strip() for line in f.readlines() if line.strip()]
|
||||||
|
|
||||||
if not lines:
|
if not lines:
|
||||||
return (0, 0, 0.0)
|
return (0, 0, 0.0, 0)
|
||||||
|
|
||||||
|
# Reset sequence if resolution changes
|
||||||
|
if self.last_start_resolution != resolution:
|
||||||
|
try:
|
||||||
|
self.current_index = lines.index(resolution)
|
||||||
|
except ValueError:
|
||||||
|
self.current_index = 0
|
||||||
|
self.current_count = 0
|
||||||
|
self.last_start_resolution = resolution
|
||||||
|
|
||||||
if selection_mode == "manual":
|
if selection_mode == "manual":
|
||||||
selected_line = resolution
|
selected_line = resolution
|
||||||
elif selection_mode == "sequential":
|
elif selection_mode == "sequential":
|
||||||
if self.__class__._current_count >= repeat_count:
|
if self.current_count >= repeat_count:
|
||||||
self.__class__._current_index += 1
|
self.current_index += 1
|
||||||
self.__class__._current_count = 0
|
self.current_count = 0
|
||||||
selected_line = lines[self.__class__._current_index % len(lines)]
|
selected_line = lines[self.current_index % len(lines)]
|
||||||
self.__class__._current_count += 1
|
self.current_count += 1
|
||||||
elif selection_mode == "random":
|
elif selection_mode == "random":
|
||||||
if self.__class__._current_count >= repeat_count or self.__class__._last_selection is None:
|
if self.current_count >= repeat_count or self.last_selection is None:
|
||||||
self.__class__._last_selection = random.choice(lines)
|
# Use a local Random instance to ensure randomness regardless of global seed
|
||||||
self.__class__._current_count = 0
|
rng = random.Random()
|
||||||
selected_line = self.__class__._last_selection
|
self.last_selection = rng.choice(lines)
|
||||||
self.__class__._current_count += 1
|
self.current_count = 0
|
||||||
|
selected_line = self.last_selection
|
||||||
|
self.current_count += 1
|
||||||
else:
|
else:
|
||||||
selected_line = resolution
|
selected_line = resolution
|
||||||
|
|
||||||
@@ -74,6 +88,6 @@ class ResolutionReader:
|
|||||||
width = int(parts[0])
|
width = int(parts[0])
|
||||||
height = int(parts[1])
|
height = int(parts[1])
|
||||||
upscale = float(parts[2])
|
upscale = float(parts[2])
|
||||||
return (width, height, upscale)
|
return (width, height, upscale, len(lines))
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
return (0, 0, 0.0)
|
return (0, 0, 0.0, len(lines))
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
1280,720,3.0
|
1280,720,3.0
|
||||||
720,1280,3.0
|
720,1280,3.0
|
||||||
1376,576,2.5
|
1376,576,2.5
|
||||||
|
576,1376,2.5
|
||||||
1024,1024,2.0
|
1024,1024,2.0
|
||||||
|
1280,800,2.7
|
||||||
|
1125,750,2.0
|
||||||
|
750,1125,2.0
|
||||||
Reference in New Issue
Block a user