import folder_paths import hashlib import json import os import requests import shutil def get_preview_path(name, type): file_name = os.path.splitext(name)[0] file_path = folder_paths.get_full_path(type, name) if file_path is None: print(f"Unable to get path for {type} {name}") return None file_path_no_ext = os.path.splitext(file_path)[0] item_image=None for ext in ["png", "jpg", "jpeg", "preview.png"]: has_image = os.path.isfile(file_path_no_ext + "." + ext) if has_image: item_image = f"{file_name}.{ext}" break return has_image, item_image def copy_preview_to_temp(file_name): if file_name is None: return None, None base_name = os.path.basename(file_name) lora_less = "/".join(file_name.split("/")[1:]) file_path = folder_paths.get_full_path("loras", lora_less) temp_path = folder_paths.get_temp_directory() preview_path = os.path.join(temp_path, "lora_preview") if not os.path.isdir(preview_path) : os.makedirs(preview_path) preview_path = os.path.join(preview_path, base_name) shutil.copyfile(file_path, preview_path) return preview_path, base_name # add previews in selectors def populate_items(names, type): for idx, item_name in enumerate(names): has_image, item_image = get_preview_path(item_name, type) names[idx] = { "content": item_name, "image": f"{type}/{item_image}" if has_image else None, "type": "loras", } names.sort(key=lambda i: i["content"].lower()) def load_json_from_file(file_path): try: with open(file_path, 'r') as json_file: data = json.load(json_file) return data except FileNotFoundError: print(f"File not found: {file_path}") return None except json.JSONDecodeError: print(f"Error decoding JSON in file: {file_path}") return None def save_dict_to_json(data_dict, file_path): try: with open(file_path, 'w') as json_file: json.dump(data_dict, json_file, indent=4) print(f"Data saved to {file_path}") except Exception as e: print(f"Error saving JSON to file: {e}") def get_model_version_info(hash_value): api_url = f"https://civitai.com/api/v1/model-versions/by-hash/{hash_value}" response = requests.get(api_url) if response.status_code == 200: return response.json() else: return None def calculate_sha256(file_path): sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(4096), b""): sha256_hash.update(chunk) return sha256_hash.hexdigest() def load_and_save_tags(lora_name, force_fetch): json_tags_path = "./loras_tags.json" lora_tags = load_json_from_file(json_tags_path) output_tags = lora_tags.get(lora_name, None) if lora_tags is not None else None if output_tags is not None: output_tags_list = output_tags else: output_tags_list = [] lora_path = folder_paths.get_full_path("loras", lora_name) if lora_tags is None or force_fetch or output_tags is None: # search on civitai only if no local cache or forced print("[Lora-Auto-Trigger] calculating lora hash") LORAsha256 = calculate_sha256(lora_path) print("[Lora-Auto-Trigger] requesting infos") model_info = get_model_version_info(LORAsha256) if model_info is not None: if "trainedWords" in model_info: print("[Lora-Auto-Trigger] tags found!") if lora_tags is None: lora_tags = {} lora_tags[lora_name] = model_info["trainedWords"] save_dict_to_json(lora_tags, json_tags_path) output_tags_list = model_info["trainedWords"] else: print("[Lora-Auto-Trigger] No informations found.") if lora_tags is None: lora_tags = {} lora_tags[lora_name] = [] save_dict_to_json(lora_tags,json_tags_path) return output_tags_list def show_list(list_input): i = 0 output = "" for debug in list_input: output += f"{i} : {debug}\n" i+=1 return output def get_metadata(filepath, type): filepath = folder_paths.get_full_path(type, filepath) with open(filepath, "rb") as file: # https://github.com/huggingface/safetensors#format # 8 bytes: N, an unsigned little-endian 64-bit integer, containing the size of the header header_size = int.from_bytes(file.read(8), "little", signed=False) if header_size <= 0: raise BufferError("Invalid header size") header = file.read(header_size) if header_size <= 0: raise BufferError("Invalid header") header_json = json.loads(header) return header_json["__metadata__"] if "__metadata__" in header_json else None # parse the __metadata__ json looking for trained tags def sort_tags_by_frequency(meta_tags): if meta_tags is None: return [] if "ss_tag_frequency" in meta_tags: meta_tags = meta_tags["ss_tag_frequency"] meta_tags = json.loads(meta_tags) sorted_tags = {} for _, dataset in meta_tags.items(): for tag, count in dataset.items(): tag = str(tag).strip() if tag in sorted_tags: sorted_tags[tag] = sorted_tags[tag] + count else: sorted_tags[tag] = count # sort tags by training frequency. Most seen tags firsts sorted_tags = dict(sorted(sorted_tags.items(), key=lambda item: item[1], reverse=True)) return list(sorted_tags.keys()) else: return [] def parse_selector(selector, tags_list): if len(tags_list) == 0: return "" range_index_list = selector.split(",") output = {} for range_index in range_index_list: # single value if range_index.count(":") == 0: # remove empty values if range_index.strip() == "": continue index = int(range_index) # ignore out of bound indexes if abs(index) > len(tags_list) - 1: continue output[index] = tags_list[index] # actual range if range_index.count(":") == 1: indexes = range_index.split(":") # check empty if indexes[0] == "": start = 0 else: start = int(indexes[0]) if indexes[1] == "": end = len(tags_list) else: end = int(indexes[1]) # check negative if start < 0: start = len(tags_list) + start if end < 0: end = len(tags_list) + end # clamp start and end values within list boundaries start, end = min(start, len(tags_list)), min(end, len(tags_list)) start, end = max(start, 0), max(end, 0) # merge all for i in range(start, end): output[i] = tags_list[i] return ", ".join(list(output.values())) def append_lora_name_if_empty(tags_list, lora_path, enabled): if not enabled or len(tags_list) > 0: return tags_list filename = os.path.splitext(lora_path)[0] filename = os.path.basename(filename) tags_list.append(filename) return tags_list