You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
11 KiB
339 lines
11 KiB
import shutil |
|
import folder_paths |
|
import os, sys |
|
import subprocess |
|
|
|
|
|
sys.path.append('../..') |
|
|
|
from torchvision.datasets.utils import download_url |
|
|
|
# ensure .js |
|
print("### Loading: ComfyUI-Manager (V0.1)") |
|
|
|
comfy_path = os.path.dirname(folder_paths.__file__) |
|
custom_nodes_path = os.path.join(comfy_path, 'custom_nodes') |
|
js_path = os.path.join(comfy_path, "web", "extensions") |
|
|
|
comfyui_manager_path = os.path.dirname(__file__) |
|
local_db_model = os.path.join(comfyui_manager_path, "model-list.json") |
|
local_db_alter = os.path.join(comfyui_manager_path, "alter-list.json") |
|
local_db_custom_node_list = os.path.join(comfyui_manager_path, "custom-node-list.json") |
|
|
|
|
|
async def get_data(uri): |
|
print(f"FECTH DATA from: {uri}") |
|
if uri.startswith("http"): |
|
async with aiohttp.ClientSession() as session: |
|
async with session.get(uri) as resp: |
|
json_text = await resp.text() |
|
else: |
|
with open(uri, "r") as f: |
|
json_text = f.read() |
|
|
|
json_obj = json.loads(json_text) |
|
return json_obj |
|
|
|
|
|
def setup_js(): |
|
# remove garbage |
|
old_js_path = os.path.join(comfy_path, "web", "extensions", "core", "comfyui-manager.js") |
|
if os.path.exists(old_js_path): |
|
os.remove(old_js_path) |
|
|
|
# setup js |
|
js_dest_path = os.path.join(js_path, "comfyui-manager") |
|
if not os.path.exists(js_dest_path): |
|
os.makedirs(js_dest_path) |
|
js_src_path = os.path.join(comfyui_manager_path, "js", "comfyui-manager.js") |
|
shutil.copy(js_src_path, js_dest_path) |
|
|
|
setup_js() |
|
|
|
|
|
# Expand Server api |
|
|
|
import server |
|
from aiohttp import web |
|
import aiohttp |
|
import json |
|
import zipfile |
|
import urllib.request |
|
|
|
|
|
def get_model_path(data): |
|
if data['save_path'] != 'default': |
|
base_model = os.path.join(folder_paths.models_dir, data['save_path']) |
|
else: |
|
model_type = data['type'] |
|
if model_type == "checkpoints": |
|
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0] |
|
elif model_type == "unclip": |
|
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0] |
|
elif model_type == "VAE": |
|
base_model = folder_paths.folder_names_and_paths["vae"][0][0] |
|
elif model_type == "lora": |
|
base_model = folder_paths.folder_names_and_paths["loras"][0][0] |
|
elif model_type == "T2I-Adapter": |
|
base_model = folder_paths.folder_names_and_paths["controlnet"][0][0] |
|
elif model_type == "T2I-Style": |
|
base_model = folder_paths.folder_names_and_paths["controlnet"][0][0] |
|
elif model_type == "controlnet": |
|
base_model = folder_paths.folder_names_and_paths["controlnet"][0][0] |
|
elif model_type == "clip_vision": |
|
base_model = folder_paths.folder_names_and_paths["clip_vision"][0][0] |
|
elif model_type == "gligen": |
|
base_model = folder_paths.folder_names_and_paths["gligen"][0][0] |
|
elif model_type == "upscale": |
|
base_model = folder_paths.folder_names_and_paths["upscale_models"][0][0] |
|
else: |
|
base_model = None |
|
|
|
return os.path.join(base_model, data['filename']) |
|
|
|
|
|
def check_a_custom_node_installed(item): |
|
item['installed'] = 'None' |
|
|
|
if item['install_type'] == 'git-clone' and len(item['files']) == 1: |
|
dir_name = os.path.splitext(os.path.basename(item['files'][0]))[0].replace(".git", "") |
|
dir_path = os.path.join(custom_nodes_path, dir_name) |
|
if os.path.exists(dir_path): |
|
item['installed'] = 'True' |
|
else: |
|
item['installed'] = 'False' |
|
|
|
elif item['install_type'] == 'copy' and len(item['files']) == 1: |
|
dir_name = os.path.basename(item['files'][0]) |
|
base_path = custom_nodes_path if item['files'][0].endswith('.py') else js_path |
|
file_path = os.path.join(base_path, dir_name) |
|
if os.path.exists(file_path): |
|
item['installed'] = 'True' |
|
else: |
|
item['installed'] = 'False' |
|
|
|
|
|
def check_custom_nodes_installed(json_obj): |
|
for item in json_obj['custom_nodes']: |
|
check_a_custom_node_installed(item) |
|
|
|
|
|
@server.PromptServer.instance.routes.get("/customnode/getlist") |
|
async def fetch_customnode_list(request): |
|
if request.rel_url.query["mode"] == "local": |
|
uri = local_db_custom_node_list |
|
else: |
|
uri = 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json' |
|
|
|
json_obj = await get_data(uri) |
|
check_custom_nodes_installed(json_obj) |
|
|
|
return web.json_response(json_obj, content_type='application/json') |
|
|
|
|
|
@server.PromptServer.instance.routes.get("/alternatives/getlist") |
|
async def fetch_alternatives_list(request): |
|
if request.rel_url.query["mode"] == "local": |
|
uri1 = local_db_alter |
|
uri2 = local_db_custom_node_list |
|
else: |
|
uri1 = 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/alter-list.json' |
|
uri2 = 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/custom-node-list.json' |
|
|
|
alter_json = await get_data(uri1) |
|
custom_node_json = await get_data(uri2) |
|
|
|
fileurl_to_custom_node = {} |
|
for item in custom_node_json['custom_nodes']: |
|
for fileurl in item['files']: |
|
fileurl_to_custom_node[fileurl] = item |
|
|
|
for item in alter_json['items']: |
|
fileurl = item['id'] |
|
if fileurl in fileurl_to_custom_node: |
|
custom_node = fileurl_to_custom_node[fileurl] |
|
check_a_custom_node_installed(custom_node) |
|
item['custom_node'] = custom_node |
|
|
|
return web.json_response(alter_json, content_type='application/json') |
|
|
|
|
|
def check_model_installed(json_obj): |
|
for item in json_obj['models']: |
|
item['installed'] = 'None' |
|
|
|
model_path = get_model_path(item) |
|
|
|
if model_path is not None: |
|
if os.path.exists(model_path): |
|
item['installed'] = 'True' |
|
else: |
|
item['installed'] = 'False' |
|
|
|
|
|
@server.PromptServer.instance.routes.get("/externalmodel/getlist") |
|
async def fetch_externalmodel_list(request): |
|
if request.rel_url.query["mode"] == "local": |
|
uri = local_db_model |
|
else: |
|
uri = 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main/model-list.json' |
|
|
|
|
|
json_obj = await get_data(uri) |
|
check_model_installed(json_obj) |
|
|
|
return web.json_response(json_obj, content_type='application/json') |
|
|
|
|
|
def unzip_install(files): |
|
temp_filename = 'manager-temp.zip' |
|
for url in files: |
|
try: |
|
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} |
|
|
|
req = urllib.request.Request(url, headers=headers) |
|
response = urllib.request.urlopen(req) |
|
data = response.read() |
|
|
|
with open(temp_filename, 'wb') as f: |
|
f.write(data) |
|
|
|
with zipfile.ZipFile(temp_filename, 'r') as zip_ref: |
|
zip_ref.extractall(custom_nodes_path) |
|
|
|
os.remove(temp_filename) |
|
except Exception as e: |
|
print(f"Install(unzip) error: {url} / {e}") |
|
return False |
|
|
|
print("Installation successful.") |
|
return True |
|
|
|
|
|
def download_url_with_agent(url, save_path): |
|
try: |
|
headers = { |
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} |
|
|
|
req = urllib.request.Request(url, headers=headers) |
|
response = urllib.request.urlopen(req) |
|
data = response.read() |
|
|
|
with open(save_path, 'wb') as f: |
|
f.write(data) |
|
|
|
except Exception as e: |
|
print(f"Download error: {url} / {e}") |
|
return False |
|
|
|
print("Installation successful.") |
|
return True |
|
|
|
|
|
def copy_install(files, js_path_name=None): |
|
for url in files: |
|
try: |
|
if url.endswith(".py"): |
|
download_url(url, custom_nodes_path) |
|
else: |
|
path = os.path.join(js_path, js_path_name) if js_path_name is not None else js_path |
|
if not os.path.exists(path): |
|
os.makedirs(path) |
|
download_url(url, path) |
|
|
|
except Exception as e: |
|
print(f"Install(copy) error: {url} / {e}") |
|
return False |
|
|
|
print("Installation successful.") |
|
return True |
|
|
|
|
|
def gitclone_install(files): |
|
print(f"install: {files}") |
|
for url in files: |
|
try: |
|
print(f"Download: git clone '{url}'") |
|
clone_cmd = ["git", "clone", url] |
|
code = subprocess.run(clone_cmd, cwd=custom_nodes_path) |
|
|
|
if code.returncode != 0: |
|
print(f"git-clone failed: {url}") |
|
return False |
|
|
|
repo_name = os.path.splitext(os.path.basename(url))[0] |
|
repo_path = os.path.join(custom_nodes_path, repo_name) |
|
|
|
install_script_path = os.path.join(repo_path, "install.py") |
|
requirements_path = os.path.join(repo_path, "requirements.txt") |
|
|
|
if os.path.exists(requirements_path): |
|
print(f"Install: pip packages") |
|
install_cmd = [sys.executable, "-m", "pip", "install", "-r", "requirements.txt"] |
|
code = subprocess.run(install_cmd, cwd=repo_path) |
|
|
|
if code.returncode != 0: |
|
print(f"install script failed: {url}") |
|
return False |
|
|
|
if os.path.exists(install_script_path): |
|
print(f"Install: install script") |
|
install_cmd = [sys.executable, "install.py"] |
|
code = subprocess.run(install_cmd, cwd=repo_path) |
|
|
|
if code.returncode != 0: |
|
print(f"install script failed: {url}") |
|
return False |
|
|
|
except Exception as e: |
|
print(f"Install(git-clone) error: {url} / {e}") |
|
return False |
|
|
|
print("Installation successful.") |
|
return True |
|
|
|
|
|
@server.PromptServer.instance.routes.post("/customnode/install") |
|
async def install_custom_node(request): |
|
json_data = await request.json() |
|
|
|
install_type = json_data['install_type'] |
|
|
|
print(f"Install custom node '{json_data['title']}'") |
|
|
|
res = False |
|
|
|
if install_type == "unzip": |
|
res = unzip_install(json_data['files']) |
|
|
|
if install_type == "copy": |
|
js_path_name = json_data['js_path'] if 'js_path' in json_data else None |
|
res = copy_install(json_data['files'], js_path_name) |
|
|
|
elif install_type == "git-clone": |
|
res = gitclone_install(json_data['files']) |
|
|
|
if res: |
|
return web.json_response({}, content_type='application/json') |
|
|
|
return web.Response(status=400) |
|
|
|
|
|
@server.PromptServer.instance.routes.post("/model/install") |
|
async def install_model(request): |
|
json_data = await request.json() |
|
|
|
model_path = get_model_path(json_data) |
|
|
|
res = False |
|
|
|
if model_path is not None: |
|
print(f"Install model '{json_data['name']}' into '{model_path}'") |
|
res = download_url_with_agent(json_data['url'], model_path) |
|
else: |
|
print(f"Model installation error: invalid model type - {json_data['type']}") |
|
|
|
if res: |
|
return web.json_response({}, content_type='application/json') |
|
|
|
return web.Response(status=400)
|
|
|