pythongosssss / ComfyUI-Custom-Scripts

Enhancements & experiments for ComfyUI, mostly focusing on UI features
MIT License
1.65k stars 124 forks source link

Set preview images for all `lora` at once, and display preview images on `autocomplete lora text` #180

Open mijuku233 opened 7 months ago

mijuku233 commented 7 months ago

Is there a way to set previews for all loras at once? I have too many loras, and setting previews for them one by one is cumbersome and time-consuming.

In addition, when the mouse is hovering over Autocomplete lora text, It would be useful to be able to see a preview!

ghostsquad commented 7 months ago

came here because I just ended up downloading a bunch more, and, yes... it's a pain.

marduk191 commented 6 months ago

civit link auto downloads png previews and json files. These integrate perfectly with it.

ghostsquad commented 6 months ago

civit link auto downloads png previews and json files. These integrate perfectly with it.

Sorry, could you be more specific? Which node downloads the previews?

pksebben commented 4 months ago

So, I ended up writing a janky lil' python script to handle image previews, if anyone here is handy with python and wants something they can kinda just run.

@pythongosssss I'd be happy to upstream this (after making it like, nicer) if you're interested, but I'd need to know what your preference is for activation - like, where should I put the button and how should this be triggered. My immediate thought is to have a checkbox somewhere that enables running on startup or something of that nature.

Also, how do you prefer your PRs? Forks I'm guessing?

Note if anyone plans on using this: You have to either get an api key from civitai and put that where it says REDACTED or just delete the authorization from the header entirely (which if I understood correctly would mean some models won't allow you to download their data?)

usage: python script.py <path-to-models-dir>

import os
import hashlib
from pathlib import Path
import requests
import sys
import json

def get_file_hash(file_path):
    hasher = hashlib.sha256()  # Using SHA256 for hashing
    with open(file_path, "rb") as file:
        chunk = file.read(4096)
        while chunk:
            hasher.update(chunk)
            chunk = file.read(4096)
    return hasher.hexdigest()

def get_model_image_url(file_path):
    file_hash = get_file_hash(file_path)
    api_url = f"https://civitai.com/api/v1/model-versions/by-hash/{file_hash}"
    headers = {"Content-Type": "application/json", "Authorization": "Bearer REDACTED"}
    response = requests.get(api_url, headers=headers)

    try:
        return response.json()["images"][0]["url"]
    except Exception as e:
        print(f"could not establish image url: {e}")

def save_model_preview_image(file_path):
    preview_path = file_path.with_suffix(".preview.png")
    if Path(preview_path).exists():
        print(f"{preview_path} exists.   Skipping...")
    else:
        image_url = get_model_image_url(file_path)
        if image_url is not None:
            response = requests.get(image_url)
            if response.status_code == 200:
                with open(preview_path, "wb") as f:
                    f.write(response.content)
        else:
            print(f"No image URL; Couldn't save image for {file_path}")

def fetch_and_save_previews(directory):
    # Walk through all directories and files in the specified directory
    for root, dirs, files in os.walk(directory):
        for filename in files:
            # Check if the file is a .safetensors file
            if filename.endswith(".safetensors"):
                file_path = Path(root) / filename
                save_model_preview_image(file_path)

if __name__ == "__main__":
    file_path = sys.argv[1]
    fetch_and_save_previews(file_path)
ghostsquad commented 4 months ago

this is good, yet, I feel like most of this logic already exists, and should be leveraged from here: https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/main/py/model_info.py

That way we get the previews, we get the .sha256 files, etc.

EDIT: Sorry, wrong place. Need to recheck, I thought this was all done in python somewhere.

ghostsquad commented 4 months ago

My bad. This all happens in JS here: https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/3f2c021e50be2fed3c9d1552ee8dcaae06ad1fe5/web/js/common/modelInfoDialog.js#L211

https://github.com/pythongosssss/ComfyUI-Custom-Scripts/blob/3f2c021e50be2fed3c9d1552ee8dcaae06ad1fe5/web/js/common/modelInfoDialog.js#L270-L290

ghostsquad commented 4 months ago

@pksebben in the short term, this could be just a regular node. Not something that would run often, but loading up an empty workflow with just this one thing would be fine. Another idea for where it could be used is in the ComfyUI Manager as another button there.

ghostsquad commented 4 months ago

@pksebben I made some improvements to the script. Saving/Reading the .sha256 file in order to avoid hashing each model on every run.

import os
import hashlib
from pathlib import Path
import requests
import sys

def get_and_save_file_hash(file_path):
    sha256_path = file_path.with_suffix(".sha256")
    if Path(sha256_path).exists():
        print(f"existing hash found: {sha256_path}")
        with open(sha256_path, "r") as file:
            return str(file.read()).strip()

    print(f"hashing: {file_path}")
    hasher = hashlib.sha256()  # Using SHA256 for hashing
    with open(file_path, "rb") as file:
        chunk = file.read(4096)
        while chunk:
            hasher.update(chunk)
            chunk = file.read(4096)
    file_hash = str(hasher.hexdigest())

    with open(sha256_path, "w") as file:
        file.write(file_hash)

    return file_hash

def get_model_image_url(file_hash):
    api_url = f"https://civitai.com/api/v1/model-versions/by-hash/{file_hash}"
    print(f"getting model info from: {api_url}")
    headers = {"Content-Type": "application/json", "Authorization": "Bearer REDACTED"}
    response = requests.get(api_url, headers=headers)

    try:
        return response.json()["images"][0]["url"]
    except Exception as e:
        print(f"could not establish image url: {e}")

def save_model_meta(file_path):
    preview_path = file_path.with_suffix(".preview.png")
    file_hash = get_and_save_file_hash(file_path)
    if Path(preview_path).exists():
        print(f"{preview_path} exists.   Skipping...")
    else:
        image_url = get_model_image_url(file_hash)
        if image_url is not None:
            response = requests.get(image_url)
            if response.status_code == 200:
                with open(preview_path, "wb") as f:
                    f.write(response.content)
        else:
            print(f"No image URL; Couldn't save image for {file_path}")

def fetch_and_save_previews(directory):
    # Walk through all directories and files in the specified directory
    for root, _, files in os.walk(directory):
        for filename in files:
            # Check if the file is a .safetensors file
            if filename.endswith(".safetensors"):
                file_path = Path(root) / filename
                print(f"→ {file_path}")
                save_model_meta(file_path)

if __name__ == "__main__":
    file_path = sys.argv[1]
    fetch_and_save_previews(file_path)