import json import os import random class CharacterJsonReader: @classmethod def INPUT_TYPES(s): base_path = os.path.dirname(os.path.realpath(__file__)) char_dir = os.path.join(base_path, "characters") characters = [] if os.path.exists(char_dir): characters = sorted([f for f in os.listdir(char_dir) if f.endswith('.json')]) return { "required": { "character_file": (characters, ), "selection_mode": (["manual", "sequential", "random"], {"default": "manual"}), "repeat_count": ("INT", {"default": 1, "min": 1, "max": 100, "step": 1}), } } @classmethod def IS_CHANGED(s, character_file, selection_mode, repeat_count): if selection_mode == "random" or selection_mode == "sequential": return float("nan") return character_file RETURN_TYPES = ( "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", "INT" ) RETURN_NAMES = ( "name", "base_specs", "hair", "eyes", "expression", "hands", "arms", "torso", "pelvis", "legs", "feet", "distinguishing_marks", "inner_layer", "outer_layer", "lower_body", "footwear", "gloves", "accessories", "aesthetic", "primary_color", "secondary_color", "tertiary_color", "lora_name", "lora_weight", "lora_triggers", "total_characters" ) FUNCTION = "read_character" CATEGORY = "AODH Pack" def __init__(self): self.current_index = 0 self.current_count = 0 self.last_selection = None self.last_start_file = None 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__)) char_dir = os.path.join(base_path, "characters") characters = sorted([f for f in os.listdir(char_dir) if f.endswith('.json')]) if not characters: 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": selected_file = character_file elif selection_mode == "sequential": if self.current_count >= repeat_count: self.current_index += 1 self.current_count = 0 selected_file = characters[self.current_index % len(characters)] self.current_count += 1 elif selection_mode == "random": if self.current_count >= repeat_count or self.last_selection is None or self.last_selection not in characters: # Use a local Random instance to ensure randomness regardless of global seed rng = random.Random() self.last_selection = rng.choice(characters) self.current_count = 0 selected_file = self.last_selection self.current_count += 1 else: selected_file = character_file file_path = os.path.join(char_dir, selected_file) with open(file_path, 'r') as f: data = json.load(f) identity = data.get("identity", {}) wardrobe = data.get("wardrobe", {}) styles = data.get("styles", {}) lora = data.get("lora", {}) def get_val(obj, key, prepend_comma=True): val = obj.get(key, "") if val and prepend_comma and not val.startswith(","): val = ", " + val return val return ( get_val(data, "character_id", prepend_comma=False), get_val(identity, "base_specs"), get_val(identity, "hair"), get_val(identity, "eyes"), get_val(identity, "expression"), get_val(identity, "hands"), get_val(identity, "arms"), get_val(identity, "torso"), get_val(identity, "pelvis"), get_val(identity, "legs"), get_val(identity, "feet"), get_val(identity, "distinguishing_marks"), get_val(wardrobe, "inner_layer"), get_val(wardrobe, "outer_layer"), get_val(wardrobe, "lower_body"), get_val(wardrobe, "footwear"), get_val(wardrobe, "gloves"), get_val(wardrobe, "accessories"), get_val(styles, "aesthetic"), get_val(styles, "primary_color"), get_val(styles, "secondary_color"), get_val(styles, "tertiary_color"), get_val(lora, "lora_name", prepend_comma=False), float(lora.get("lora_weight", 0.0) if lora.get("lora_weight") else 0.0), get_val(lora, "lora_triggers"), len(characters), )