CoolFace
Apppublic

fred-dev/comfy_ui_ali

sourceHugging Facemitupdated 1y agoView on Hugging Face
0likes
manager_server.py1766 linesDownload Raw Back to glob
1import traceback2 3import folder_paths4import locale5import subprocess  # don't remove this6import concurrent7import nodes8import os9import sys10import threading11import re12import shutil13import git14from datetime import datetime15 16from server import PromptServer17import manager_core as core18import manager_util19import cm_global20import logging21import asyncio22import queue23 24import manager_downloader25 26 27logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")28logging.info("[ComfyUI-Manager] network_mode: " + core.get_config()['network_mode'])29 30comfy_ui_hash = "-"31comfyui_tag = None32 33SECURITY_MESSAGE_MIDDLE_OR_BELOW = "ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"34SECURITY_MESSAGE_NORMAL_MINUS = "ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"35SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"36SECURITY_MESSAGE_NORMAL_MINUS_MODEL = "ERROR: Downloading models that are not in '.safetensors' format is only allowed for models registered in the 'default' channel at this security level. If you want to download this model, set the security level to 'normal-' or lower."37 38routes = PromptServer.instance.routes39 40def handle_stream(stream, prefix):41    stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')42    for msg in stream:43        if prefix == '[!]' and ('it/s]' in msg or 's/it]' in msg) and ('%|' in msg or 'it [' in msg):44            if msg.startswith('100%'):45                print('\r' + msg, end="", file=sys.stderr),46            else:47                print('\r' + msg[:-1], end="", file=sys.stderr),48        else:49            if prefix == '[!]':50                print(prefix, msg, end="", file=sys.stderr)51            else:52                print(prefix, msg, end="")53 54 55from comfy.cli_args import args56import latent_preview57 58def is_loopback(address):59    import ipaddress60    try:61        return ipaddress.ip_address(address).is_loopback62    except ValueError:63        return False64 65is_local_mode = is_loopback(args.listen)66 67 68model_dir_name_map = {69    "checkpoints": "checkpoints",70    "checkpoint": "checkpoints",71    "unclip": "checkpoints",72    "text_encoders": "text_encoders",73    "clip": "text_encoders",74    "vae": "vae",75    "lora": "loras",76    "t2i-adapter": "controlnet",77    "t2i-style": "controlnet",78    "controlnet": "controlnet",79    "clip_vision": "clip_vision",80    "gligen": "gligen",81    "upscale": "upscale_models",82    "embedding": "embeddings",83    "embeddings": "embeddings",84    "unet": "diffusion_models",85    "diffusion_model": "diffusion_models",86}87 88 89def is_allowed_security_level(level):90    if level == 'block':91        return False92    elif level == 'high':93        if is_local_mode:94            return core.get_config()['security_level'] in ['weak', 'normal-']95        else:96            return core.get_config()['security_level'] == 'weak'97    elif level == 'middle':98        return core.get_config()['security_level'] in ['weak', 'normal', 'normal-']99    else:100        return True101 102 103async def get_risky_level(files, pip_packages):104    json_data1 = await core.get_data_by_mode('local', 'custom-node-list.json')105    json_data2 = await core.get_data_by_mode('cache', 'custom-node-list.json', channel_url='https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main')106 107    all_urls = set()108    for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:109        all_urls.update(x.get('files', []))110 111    for x in files:112        if x not in all_urls:113            return "high"114 115    all_pip_packages = set()116    for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:117        all_pip_packages.update(x.get('pip', []))118 119    for p in pip_packages:120        if p not in all_pip_packages:121            return "block"122 123    return "middle"124 125 126class ManagerFuncsInComfyUI(core.ManagerFuncs):127    def get_current_preview_method(self):128        if args.preview_method == latent_preview.LatentPreviewMethod.Auto:129            return "auto"130        elif args.preview_method == latent_preview.LatentPreviewMethod.Latent2RGB:131            return "latent2rgb"132        elif args.preview_method == latent_preview.LatentPreviewMethod.TAESD:133            return "taesd"134        else:135            return "none"136 137    def run_script(self, cmd, cwd='.'):138        if len(cmd) > 0 and cmd[0].startswith("#"):139            logging.error(f"[ComfyUI-Manager] Unexpected behavior: `{cmd}`")140            return 0141 142        process = subprocess.Popen(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, env=core.get_script_env())143 144        stdout_thread = threading.Thread(target=handle_stream, args=(process.stdout, ""))145        stderr_thread = threading.Thread(target=handle_stream, args=(process.stderr, "[!]"))146 147        stdout_thread.start()148        stderr_thread.start()149 150        stdout_thread.join()151        stderr_thread.join()152 153        return process.wait()154 155 156core.manager_funcs = ManagerFuncsInComfyUI()157 158sys.path.append('../..')159 160from manager_downloader import download_url, download_url_with_agent161 162core.comfy_path = os.path.dirname(folder_paths.__file__)163core.js_path = os.path.join(core.comfy_path, "web", "extensions")164 165local_db_model = os.path.join(manager_util.comfyui_manager_path, "model-list.json")166local_db_alter = os.path.join(manager_util.comfyui_manager_path, "alter-list.json")167local_db_custom_node_list = os.path.join(manager_util.comfyui_manager_path, "custom-node-list.json")168local_db_extension_node_mappings = os.path.join(manager_util.comfyui_manager_path, "extension-node-map.json")169 170 171def set_preview_method(method):172    if method == 'auto':173        args.preview_method = latent_preview.LatentPreviewMethod.Auto174    elif method == 'latent2rgb':175        args.preview_method = latent_preview.LatentPreviewMethod.Latent2RGB176    elif method == 'taesd':177        args.preview_method = latent_preview.LatentPreviewMethod.TAESD178    else:179        args.preview_method = latent_preview.LatentPreviewMethod.NoPreviews180 181    core.get_config()['preview_method'] = method182 183 184set_preview_method(core.get_config()['preview_method'])185 186 187def set_component_policy(mode):188    core.get_config()['component_policy'] = mode189 190def set_update_policy(mode):191    core.get_config()['update_policy'] = mode192 193def set_db_mode(mode):194    core.get_config()['db_mode'] = mode195 196def print_comfyui_version():197    global comfy_ui_hash198    global comfyui_tag199 200    is_detached = False201    try:202        repo = git.Repo(os.path.dirname(folder_paths.__file__))203        core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))204 205        comfy_ui_hash = repo.head.commit.hexsha206        cm_global.variables['comfyui.revision'] = core.comfy_ui_revision207 208        core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime209        cm_global.variables['comfyui.commit_datetime'] = core.comfy_ui_commit_datetime210 211        is_detached = repo.head.is_detached212        current_branch = repo.active_branch.name213 214        comfyui_tag = core.get_comfyui_tag()215 216        try:217            if not os.environ.get('__COMFYUI_DESKTOP_VERSION__') and core.comfy_ui_commit_datetime.date() < core.comfy_ui_required_commit_datetime.date():218                logging.warning(f"\n\n## [WARN] ComfyUI-Manager: Your ComfyUI version ({core.comfy_ui_revision})[{core.comfy_ui_commit_datetime.date()}] is too old. Please update to the latest version. ##\n\n")219        except:220            pass221 222        # process on_revision_detected -->223        if 'cm.on_revision_detected_handler' in cm_global.variables:224            for k, f in cm_global.variables['cm.on_revision_detected_handler']:225                try:226                    f(core.comfy_ui_revision)227                except Exception:228                    logging.error(f"[ERROR] '{k}' on_revision_detected_handler")229                    traceback.print_exc()230 231            del cm_global.variables['cm.on_revision_detected_handler']232        else:233            logging.warning("[ComfyUI-Manager] Some features are restricted due to your ComfyUI being outdated.")234        # <--235 236        if current_branch == "master":237            if comfyui_tag:238                logging.info(f"### ComfyUI Version: {comfyui_tag} | Released on '{core.comfy_ui_commit_datetime.date()}'")239            else:240                logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'")241        else:242            if comfyui_tag:243                logging.info(f"### ComfyUI Version: {comfyui_tag} on '{current_branch}' | Released on '{core.comfy_ui_commit_datetime.date()}'")244            else:245                logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} on '{current_branch}' [{comfy_ui_hash[:8]}] | Released on '{core.comfy_ui_commit_datetime.date()}'")246    except:247        if is_detached:248            logging.info(f"### ComfyUI Revision: {core.comfy_ui_revision} [{comfy_ui_hash[:8]}] *DETACHED | Released on '{core.comfy_ui_commit_datetime.date()}'")249        else:250            logging.info("### ComfyUI Revision: UNKNOWN (The currently installed ComfyUI is not a Git repository)")251 252 253print_comfyui_version()254core.check_invalid_nodes()255 256 257 258def setup_environment():259    git_exe = core.get_config()['git_exe']260 261    if git_exe != '':262        git.Git().update_environment(GIT_PYTHON_GIT_EXECUTABLE=git_exe)263 264 265setup_environment()266 267# Expand Server api268 269from aiohttp import web270import aiohttp271import json272import zipfile273import urllib.request274 275 276def get_model_dir(data, show_log=False):277    if 'download_model_base' in folder_paths.folder_names_and_paths:278        models_base = folder_paths.folder_names_and_paths['download_model_base'][0][0]279    else:280        models_base = folder_paths.models_dir281 282    # NOTE: Validate to prevent path traversal.283    if any(char in data['filename'] for char in {'/', '\\', ':'}):284        return None285 286    def resolve_custom_node(save_path):287        save_path = save_path[13:] # remove 'custom_nodes/'288 289        # NOTE: Validate to prevent path traversal.290        if save_path.startswith(os.path.sep) or ':' in save_path:291            return None292 293        repo_name = save_path.replace('\\','/').split('/')[0] # get custom node repo name294 295        # NOTE: The creation of files within the custom node path should be removed in the future.296        repo_path = core.lookup_installed_custom_nodes_legacy(repo_name)297        if repo_path is not None and repo_path[0]:298            # Returns the retargeted path based on the actually installed repository299            return os.path.join(os.path.dirname(repo_path[1]), save_path)300        else:301            return None302 303    if data['save_path'] != 'default':304        if '..' in data['save_path'] or data['save_path'].startswith('/'):305            if show_log:306                logging.info(f"[WARN] '{data['save_path']}' is not allowed path. So it will be saved into 'models/etc'.")307            base_model = os.path.join(models_base, "etc")308        else:309            if data['save_path'].startswith("custom_nodes"):310                base_model = resolve_custom_node(data['save_path'])311                if base_model is None:312                    if show_log:313                        logging.info(f"[ComfyUI-Manager] The target custom node for model download is not installed: {data['save_path']}")314                    return None315            else:316                base_model = os.path.join(models_base, data['save_path'])317    else:318        model_dir_name = model_dir_name_map.get(data['type'].lower())319        if model_dir_name is not None:320            base_model = folder_paths.folder_names_and_paths[model_dir_name][0][0]321        else:322            base_model = os.path.join(models_base, "etc")323 324    return base_model325 326 327def get_model_path(data, show_log=False):328    base_model = get_model_dir(data, show_log)329    if base_model is None:330        return None331    else:332        if data['filename'] == '<huggingface>':333            return os.path.join(base_model, os.path.basename(data['url']))334        else:335            return os.path.join(base_model, data['filename'])336 337 338def check_state_of_git_node_pack(node_packs, do_fetch=False, do_update_check=True, do_update=False):339    if do_fetch:340        print("Start fetching...", end="")341    elif do_update:342        print("Start updating...", end="")343    elif do_update_check:344        print("Start update check...", end="")345 346    def process_custom_node(item):347        core.check_state_of_git_node_pack_single(item, do_fetch, do_update_check, do_update)348 349    with concurrent.futures.ThreadPoolExecutor(4) as executor:350        for k, v in node_packs.items():351            if v.get('active_version') in ['unknown', 'nightly']:352                executor.submit(process_custom_node, v)353 354    if do_fetch:355        print("\x1b[2K\rFetching done.")356    elif do_update:357        update_exists = any(item.get('updatable', False) for item in node_packs.values())358        if update_exists:359            print("\x1b[2K\rUpdate done.")360        else:361            print("\x1b[2K\rAll extensions are already up-to-date.")362    elif do_update_check:363        print("\x1b[2K\rUpdate check done.")364 365 366def nickname_filter(json_obj):367    preemptions_map = {}368 369    for k, x in json_obj.items():370        if 'preemptions' in x[1]:371            for y in x[1]['preemptions']:372                preemptions_map[y] = k373        elif k.endswith("/ComfyUI"):374            for y in x[0]:375                preemptions_map[y] = k376 377    updates = {}378    for k, x in json_obj.items():379        removes = set()380        for y in x[0]:381            k2 = preemptions_map.get(y)382            if k2 is not None and k != k2:383                removes.add(y)384 385        if len(removes) > 0:386            updates[k] = [y for y in x[0] if y not in removes]387 388    for k, v in updates.items():389        json_obj[k][0] = v390 391    return json_obj392 393 394task_queue = queue.Queue()395nodepack_result = {}396model_result = {}397tasks_in_progress = set()398task_worker_lock = threading.Lock()399 400async def task_worker():401    global task_queue402    global nodepack_result403    global model_result404    global tasks_in_progress405 406    async def do_install(item) -> str:407        ui_id, node_spec_str, channel, mode, skip_post_install = item408 409        try:410            node_spec = core.unified_manager.resolve_node_spec(node_spec_str)411            if node_spec is None:412                logging.error(f"Cannot resolve install target: '{node_spec_str}'")413                return f"Cannot resolve install target: '{node_spec_str}'"414 415            node_name, version_spec, is_specified = node_spec416            res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install)417            # discard post install if skip_post_install mode418 419            if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:420                logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")421                return res.msg422 423            elif not res.result:424                logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")425                return res.msg426 427            return 'success'428        except Exception:429            traceback.print_exc()430            return f"Installation failed:\n{node_spec_str}"431 432    async def do_update(item):433        ui_id, node_name, node_ver = item434 435        try:436            res = core.unified_manager.unified_update(node_name, node_ver)437 438            if res.ver == 'unknown':439                url = core.unified_manager.unknown_active_nodes[node_name][0]440                title = os.path.basename(url)441            else:442                url = core.unified_manager.cnr_map[node_name].get('repository')443                title = core.unified_manager.cnr_map[node_name]['name']444 445            manager_util.clear_pip_cache()446 447            if url is not None:448                base_res = {'url': url, 'title': title}449            else:450                base_res = {'title': title}451 452            if res.result:453                if res.action == 'skip':454                    base_res['msg'] = 'skip'455                    return base_res456                else:457                    base_res['msg'] = 'success'458                    return base_res459 460            base_res['msg'] = f"An error occurred while updating '{node_name}'."461            logging.error(f"\nERROR: An error occurred while updating '{node_name}'. (res.result={res.result}, res.action={res.action})")462            return base_res463        except Exception:464            traceback.print_exc()465 466        return {'msg':f"An error occurred while updating '{node_name}'."}467 468    async def do_update_comfyui(is_stable) -> str:469        try:470            repo_path = os.path.dirname(folder_paths.__file__)471            latest_tag = None472            if is_stable:473                res, latest_tag = core.update_to_stable_comfyui(repo_path)474            else:475                res = core.update_path(repo_path)476                477            if res == "fail":478                logging.error("ComfyUI update failed")479                return "fail"480            elif res == "updated":481                if is_stable:482                    logging.info("ComfyUI is updated to latest stable version.")483                    return "success-stable-"+latest_tag484                else:485                    logging.info("ComfyUI is updated to latest nightly version.")486                    return "success-nightly"487            else:  # skipped488                logging.info("ComfyUI is up-to-date.")489                return "skip"490 491        except Exception:492            traceback.print_exc()493 494        return "An error occurred while updating 'comfyui'."495 496    async def do_fix(item) -> str:497        ui_id, node_name, node_ver = item498 499        try:500            res = core.unified_manager.unified_fix(node_name, node_ver)501 502            if res.result:503                return 'success'504            else:505                logging.error(res.msg)506 507            logging.error(f"\nERROR: An error occurred while fixing '{node_name}@{node_ver}'.")508        except Exception:509            traceback.print_exc()510 511        return f"An error occurred while fixing '{node_name}@{node_ver}'."512 513    async def do_uninstall(item) -> str:514        ui_id, node_name, is_unknown = item515 516        try:517            res = core.unified_manager.unified_uninstall(node_name, is_unknown)518 519            if res.result:520                return 'success'521 522            logging.error(f"\nERROR: An error occurred while uninstalling '{node_name}'.")523        except Exception:524            traceback.print_exc()525 526        return f"An error occurred while uninstalling '{node_name}'."527 528    async def do_disable(item) -> str:529        ui_id, node_name, is_unknown = item530 531        try:532            res = core.unified_manager.unified_disable(node_name, is_unknown)533 534            if res:535                return 'success'536 537        except Exception:538            traceback.print_exc()539 540        return f"Failed to disable: '{node_name}'"541 542    async def do_install_model(item) -> str:543        ui_id, json_data = item544 545        model_path = get_model_path(json_data)546        model_url = json_data['url']547 548        res = False549 550        try:551            if model_path is not None:552                logging.info(f"Install model '{json_data['name']}' from '{model_url}' into '{model_path}'")553 554                if json_data['filename'] == '<huggingface>':555                    if os.path.exists(os.path.join(model_path, os.path.dirname(json_data['url']))):556                        logging.error(f"[ComfyUI-Manager] the model path already exists: {model_path}")557                        return f"The model path already exists: {model_path}"558 559                    logging.info(f"[ComfyUI-Manager] Downloading '{model_url}' into '{model_path}'")560                    manager_downloader.download_repo_in_bytes(repo_id=model_url, local_dir=model_path)561 562                    return 'success'563 564                elif not core.get_config()['model_download_by_agent'] and (565                        model_url.startswith('https://github.com') or model_url.startswith('https://huggingface.co') or model_url.startswith('https://heibox.uni-heidelberg.de')):566                    model_dir = get_model_dir(json_data, True)567                    download_url(model_url, model_dir, filename=json_data['filename'])568                    if model_path.endswith('.zip'):569                        res = core.unzip(model_path)570                    else:571                        res = True572 573                    if res:574                        return 'success'575                else:576                    res = download_url_with_agent(model_url, model_path)577                    if res and model_path.endswith('.zip'):578                        res = core.unzip(model_path)579            else:580                logging.error(f"[ComfyUI-Manager] Model installation error: invalid model type - {json_data['type']}")581 582            if res:583                return 'success'584 585        except Exception as e:586            logging.error(f"[ComfyUI-Manager] ERROR: {e}", file=sys.stderr)587 588        return f"Model installation error: {model_url}"589 590    stats = {}591 592    while True:593        done_count = len(nodepack_result) + len(model_result)594        total_count = done_count + task_queue.qsize()595 596        if task_queue.empty():597            logging.info(f"\n[ComfyUI-Manager] Queued works are completed.\n{stats}")598 599            logging.info("\nAfter restarting ComfyUI, please refresh the browser.")600            PromptServer.instance.send_sync("cm-queue-status",601                                            {'status': 'done',602                                             'nodepack_result': nodepack_result, 'model_result': model_result,603                                             'total_count': total_count, 'done_count': done_count})604            nodepack_result = {}605            task_queue = queue.Queue()606            return  # terminate worker thread607 608        with task_worker_lock:609            kind, item = task_queue.get()610            tasks_in_progress.add((kind, item[0]))611 612        try:613            if kind == 'install':614                msg = await do_install(item)615            elif kind == 'install-model':616                msg = await do_install_model(item)617            elif kind == 'update':618                msg = await do_update(item)619            elif kind == 'update-main':620                msg = await do_update(item)621            elif kind == 'update-comfyui':622                msg = await do_update_comfyui(item[1])623            elif kind == 'fix':624                msg = await do_fix(item)625            elif kind == 'uninstall':626                msg = await do_uninstall(item)627            elif kind == 'disable':628                msg = await do_disable(item)629            else:630                msg = "Unexpected kind: " + kind631        except Exception:632            traceback.print_exc()633            msg = f"Exception: {(kind, item)}"634 635        with task_worker_lock:636            tasks_in_progress.remove((kind, item[0]))637 638            ui_id = item[0]639            if kind == 'install-model':640                model_result[ui_id] = msg641                ui_target = "model_manager"642            elif kind == 'update-main':643                nodepack_result[ui_id] = msg644                ui_target = "main"645            elif kind == 'update-comfyui':646                nodepack_result['comfyui'] = msg647                ui_target = "main"648            elif kind == 'update':649                nodepack_result[ui_id] = msg['msg']650                ui_target = "nodepack_manager"651            else:652                nodepack_result[ui_id] = msg653                ui_target = "nodepack_manager"654 655        stats[kind] = stats.get(kind, 0) + 1656 657        PromptServer.instance.send_sync("cm-queue-status",658                                        {'status': 'in_progress', 'target': item[0], 'ui_target': ui_target,659                                         'total_count': total_count, 'done_count': done_count})660 661 662@routes.get("/customnode/getmappings")663async def fetch_customnode_mappings(request):664    """665    provide unified (node -> node pack) mapping list666    """667    mode = request.rel_url.query["mode"]668 669    nickname_mode = False670    if mode == "nickname":671        mode = "local"672        nickname_mode = True673 674    json_obj = await core.get_data_by_mode(mode, 'extension-node-map.json')675    json_obj = core.map_to_unified_keys(json_obj)676 677    if nickname_mode:678        json_obj = nickname_filter(json_obj)679 680    all_nodes = set()681    patterns = []682    for k, x in json_obj.items():683        all_nodes.update(set(x[0]))684 685        if 'nodename_pattern' in x[1]:686            patterns.append((x[1]['nodename_pattern'], x[0]))687 688    missing_nodes = set(nodes.NODE_CLASS_MAPPINGS.keys()) - all_nodes689 690    for x in missing_nodes:691        for pat, item in patterns:692            if re.match(pat, x):693                item.append(x)694 695    return web.json_response(json_obj, content_type='application/json')696 697 698@routes.get("/customnode/fetch_updates")699async def fetch_updates(request):700    try:701        if request.rel_url.query["mode"] == "local":702            channel = 'local'703        else:704            channel = core.get_config()['channel_url']705 706        await core.unified_manager.reload(request.rel_url.query["mode"])707        await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"])708 709        res = core.unified_manager.fetch_or_pull_git_repo(is_pull=False)710 711        for x in res['failed']:712            logging.error(f"FETCH FAILED: {x}")713 714        logging.info("\nDone.")715 716        if len(res['updated']) > 0:717            return web.Response(status=201)718 719        return web.Response(status=200)720    except:721        traceback.print_exc()722        return web.Response(status=400)723 724 725@routes.get("/manager/queue/update_all")726async def update_all(request):727    if not is_allowed_security_level('middle'):728        logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)729        return web.Response(status=403)730 731    with task_worker_lock:732        is_processing = task_worker_thread is not None and task_worker_thread.is_alive()733        if is_processing:734            return web.Response(status=401)735        736    await core.save_snapshot_with_postfix('autosave')737 738    if request.rel_url.query["mode"] == "local":739        channel = 'local'740    else:741        channel = core.get_config()['channel_url']742 743    await core.unified_manager.reload(request.rel_url.query["mode"])744    await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"])745 746    for k, v in core.unified_manager.active_nodes.items():747        if k == 'comfyui-manager':748            # skip updating comfyui-manager if desktop version749            if os.environ.get('__COMFYUI_DESKTOP_VERSION__'):750                continue751 752        update_item = k, k, v[0]753        task_queue.put(("update-main", update_item))754 755    for k, v in core.unified_manager.unknown_active_nodes.items():756        if k == 'comfyui-manager':757            # skip updating comfyui-manager if desktop version758            if os.environ.get('__COMFYUI_DESKTOP_VERSION__'):759                continue760 761        update_item = k, k, 'unknown'762        task_queue.put(("update-main", update_item))763 764    return web.Response(status=200)765 766 767def convert_markdown_to_html(input_text):768    pattern_a = re.compile(r'\[a/([^]]+)]\(([^)]+)\)')769    pattern_w = re.compile(r'\[w/([^]]+)]')770    pattern_i = re.compile(r'\[i/([^]]+)]')771    pattern_bold = re.compile(r'\*\*([^*]+)\*\*')772    pattern_white = re.compile(r'%%([^*]+)%%')773 774    def replace_a(match):775        return f"<a href='{match.group(2)}' target='blank'>{match.group(1)}</a>"776 777    def replace_w(match):778        return f"<p class='cm-warn-note'>{match.group(1)}</p>"779 780    def replace_i(match):781        return f"<p class='cm-info-note'>{match.group(1)}</p>"782 783    def replace_bold(match):784        return f"<B>{match.group(1)}</B>"785 786    def replace_white(match):787        return f"<font color='white'>{match.group(1)}</font>"788 789    input_text = input_text.replace('\\[', '&#91;').replace('\\]', '&#93;').replace('<', '&lt;').replace('>', '&gt;')790 791    result_text = re.sub(pattern_a, replace_a, input_text)792    result_text = re.sub(pattern_w, replace_w, result_text)793    result_text = re.sub(pattern_i, replace_i, result_text)794    result_text = re.sub(pattern_bold, replace_bold, result_text)795    result_text = re.sub(pattern_white, replace_white, result_text)796 797    return result_text.replace("\n", "<BR>")798 799 800def populate_markdown(x):801    if 'description' in x:802        x['description'] = convert_markdown_to_html(manager_util.sanitize_tag(x['description']))803 804    if 'name' in x:805        x['name'] = manager_util.sanitize_tag(x['name'])806 807    if 'title' in x:808        x['title'] = manager_util.sanitize_tag(x['title'])809 810 811# freeze imported version812startup_time_installed_node_packs = core.get_installed_node_packs()813@routes.get("/customnode/installed")814async def installed_list(request):815    mode = request.query.get('mode', 'default')816 817    if mode == 'imported':818        res = startup_time_installed_node_packs819    else:820        res = core.get_installed_node_packs()821 822    return web.json_response(res, content_type='application/json')823 824 825@routes.get("/customnode/getlist")826async def fetch_customnode_list(request):827    """828    provide unified custom node list829    """830    if request.rel_url.query.get("skip_update", '').lower() == "true":831        skip_update = True832    else:833        skip_update = False834 835    if request.rel_url.query["mode"] == "local":836        channel = 'local'837    else:838        channel = core.get_config()['channel_url']839 840    node_packs = await core.get_unified_total_nodes(channel, request.rel_url.query["mode"], 'cache')841    json_obj_github = core.get_data_by_mode(request.rel_url.query["mode"], 'github-stats.json', 'default')842    json_obj_extras = core.get_data_by_mode(request.rel_url.query["mode"], 'extras.json', 'default')843 844    core.populate_github_stats(node_packs, await json_obj_github)845    core.populate_favorites(node_packs, await json_obj_extras)846 847    check_state_of_git_node_pack(node_packs, not skip_update, do_update_check=not skip_update)848 849    for v in node_packs.values():850        populate_markdown(v)851 852    if channel != 'local':853        found = 'custom'854 855        for name, url in core.get_channel_dict().items():856            if url == channel:857                found = name858                break859 860        channel = found861 862    result = dict(channel=channel, node_packs=node_packs)863 864    return web.json_response(result, content_type='application/json')865 866 867@routes.get("/customnode/alternatives")868async def fetch_customnode_alternatives(request):869    alter_json = await core.get_data_by_mode(request.rel_url.query["mode"], 'alter-list.json')870 871    res = {}872 873    for item in alter_json['items']:874        populate_markdown(item)875        res[item['id']] = item876 877    res = core.map_to_unified_keys(res)878 879    return web.json_response(res, content_type='application/json')880 881 882def check_model_installed(json_obj):883    def is_exists(model_dir_name, filename, url):884        if filename == '<huggingface>':885            filename = os.path.basename(url)886 887        dirs = folder_paths.get_folder_paths(model_dir_name)888 889        for x in dirs:890            if os.path.exists(os.path.join(x, filename)):891                return True892 893        return False894 895    model_dir_names = ['checkpoints', 'loras', 'vae', 'text_encoders', 'diffusion_models', 'clip_vision', 'embeddings',896                       'diffusers', 'vae_approx', 'controlnet', 'gligen', 'upscale_models', 'hypernetworks',897                       'photomaker', 'classifiers']898 899    total_models_files = set()900    for x in model_dir_names:901        for y in folder_paths.get_filename_list(x):902            total_models_files.add(y)903 904    def process_model_phase(item):905        if 'diffusion' not in item['filename'] and 'pytorch' not in item['filename'] and 'model' not in item['filename']:906            # non-general name case907            if item['filename'] in total_models_files:908                item['installed'] = 'True'909                return910 911        if item['save_path'] == 'default':912            model_dir_name = model_dir_name_map.get(item['type'].lower())913            if model_dir_name is not None:914                item['installed'] = str(is_exists(model_dir_name, item['filename'], item['url']))915            else:916                item['installed'] = 'False'917        else:918            model_dir_name = item['save_path'].split('/')[0]919            if model_dir_name in folder_paths.folder_names_and_paths:920                if is_exists(model_dir_name, item['filename'], item['url']):921                    item['installed'] = 'True'922 923            if 'installed' not in item:924                if item['filename'] == '<huggingface>':925                    filename = os.path.basename(item['url'])926                else:927                    filename = item['filename']928 929                fullpath = os.path.join(folder_paths.models_dir, item['save_path'], filename)930 931                item['installed'] = 'True' if os.path.exists(fullpath) else 'False'932 933    with concurrent.futures.ThreadPoolExecutor(8) as executor:934        for item in json_obj['models']:935            executor.submit(process_model_phase, item)936 937 938@routes.get("/externalmodel/getlist")939async def fetch_externalmodel_list(request):940    # The model list is only allowed in the default channel, yet.941    json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')942 943    check_model_installed(json_obj)944 945    for x in json_obj['models']:946        populate_markdown(x)947 948    return web.json_response(json_obj, content_type='application/json')949 950 951@PromptServer.instance.routes.get("/snapshot/getlist")952async def get_snapshot_list(request):953    items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')]954    items.sort(reverse=True)955    return web.json_response({'items': items}, content_type='application/json')956 957 958@routes.get("/snapshot/remove")959async def remove_snapshot(request):960    if not is_allowed_security_level('middle'):961        logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)962        return web.Response(status=403)963 964    try:965        target = request.rel_url.query["target"]966 967        path = os.path.join(core.manager_snapshot_path, f"{target}.json")968        if os.path.exists(path):969            os.remove(path)970 971        return web.Response(status=200)972    except:973        return web.Response(status=400)974 975 976@routes.get("/snapshot/restore")977async def restore_snapshot(request):978    if not is_allowed_security_level('middle'):979        logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)980        return web.Response(status=403)981 982    try:983        target = request.rel_url.query["target"]984 985        path = os.path.join(core.manager_snapshot_path, f"{target}.json")986        if os.path.exists(path):987            if not os.path.exists(core.manager_startup_script_path):988                os.makedirs(core.manager_startup_script_path)989 990            target_path = os.path.join(core.manager_startup_script_path, "restore-snapshot.json")991            shutil.copy(path, target_path)992 993            logging.info(f"Snapshot restore scheduled: `{target}`")994            return web.Response(status=200)995 996        logging.error(f"Snapshot file not found: `{path}`")997        return web.Response(status=400)998    except:999        return web.Response(status=400)1000 1001 1002@routes.get("/snapshot/get_current")1003async def get_current_snapshot_api(request):1004    try:1005        return web.json_response(await core.get_current_snapshot(), content_type='application/json')1006    except:1007        return web.Response(status=400)1008 1009 1010@routes.get("/snapshot/save")1011async def save_snapshot(request):1012    try:1013        await core.save_snapshot_with_postfix('snapshot')1014        return web.Response(status=200)1015    except:1016        return web.Response(status=400)1017 1018 1019def unzip_install(files):1020    temp_filename = 'manager-temp.zip'1021    for url in files:1022        if url.endswith("/"):1023            url = url[:-1]1024        try:1025            headers = {1026                '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'}1027 1028            req = urllib.request.Request(url, headers=headers)1029            response = urllib.request.urlopen(req)1030            data = response.read()1031 1032            with open(temp_filename, 'wb') as f:1033                f.write(data)1034 1035            with zipfile.ZipFile(temp_filename, 'r') as zip_ref:1036                zip_ref.extractall(core.get_default_custom_nodes_path())1037 1038            os.remove(temp_filename)1039        except Exception as e:1040            logging.error(f"Install(unzip) error: {url} / {e}", file=sys.stderr)1041            return False1042 1043    logging.info("Installation was successful.")1044    return True1045 1046 1047def copy_install(files, js_path_name=None):1048    for url in files:1049        if url.endswith("/"):1050            url = url[:-1]1051        try:1052            filename = os.path.basename(url)1053            if url.endswith(".py"):1054                download_url(url, core.get_default_custom_nodes_path(), filename)1055            else:1056                path = os.path.join(core.js_path, js_path_name) if js_path_name is not None else core.js_path1057                if not os.path.exists(path):1058                    os.makedirs(path)1059                download_url(url, path, filename)1060 1061        except Exception as e:1062            logging.error(f"Install(copy) error: {url} / {e}", file=sys.stderr)1063            return False1064 1065    logging.info("Installation was successful.")1066    return True1067 1068 1069def copy_uninstall(files, js_path_name='.'):1070    for url in files:1071        if url.endswith("/"):1072            url = url[:-1]1073        dir_name = os.path.basename(url)1074        base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name)1075        file_path = os.path.join(base_path, dir_name)1076 1077        try:1078            if os.path.exists(file_path):1079                os.remove(file_path)1080            elif os.path.exists(file_path + ".disabled"):1081                os.remove(file_path + ".disabled")1082        except Exception as e:1083            logging.error(f"Uninstall(copy) error: {url} / {e}", file=sys.stderr)1084            return False1085 1086    logging.info("Uninstallation was successful.")1087    return True1088 1089 1090def copy_set_active(files, is_disable, js_path_name='.'):1091    if is_disable:1092        action_name = "Disable"1093    else:1094        action_name = "Enable"1095 1096    for url in files:1097        if url.endswith("/"):1098            url = url[:-1]1099        dir_name = os.path.basename(url)1100        base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name)1101        file_path = os.path.join(base_path, dir_name)1102 1103        try:1104            if is_disable:1105                current_name = file_path1106                new_name = file_path + ".disabled"1107            else:1108                current_name = file_path + ".disabled"1109                new_name = file_path1110 1111            os.rename(current_name, new_name)1112 1113        except Exception as e:1114            logging.error(f"{action_name}(copy) error: {url} / {e}", file=sys.stderr)1115 1116            return False1117 1118    logging.info(f"{action_name} was successful.")1119    return True1120 1121 1122@routes.get("/customnode/versions/{node_name}")1123async def get_cnr_versions(request):1124    node_name = request.match_info.get("node_name", None)1125    versions = core.cnr_utils.all_versions_of_node(node_name)1126 1127    if versions is not None:1128        return web.json_response(versions, content_type='application/json')1129 1130    return web.Response(status=400)1131 1132 1133@routes.get("/customnode/disabled_versions/{node_name}")1134async def get_disabled_versions(request):1135    node_name = request.match_info.get("node_name", None)1136    versions = []1137    if node_name in core.unified_manager.nightly_inactive_nodes:1138        versions.append(dict(version='nightly'))1139 1140    for v in core.unified_manager.cnr_inactive_nodes.get(node_name, {}).keys():1141        versions.append(dict(version=v))1142 1143    if versions:1144        return web.json_response(versions, content_type='application/json')1145 1146    return web.Response(status=400)1147 1148 1149@routes.post("/customnode/import_fail_info")1150async def import_fail_info(request):1151    json_data = await request.json()1152 1153    if 'cnr_id' in json_data:1154        module_name = core.unified_manager.get_module_name(json_data['cnr_id'])1155    else:1156        module_name = core.unified_manager.get_module_name(json_data['url'])1157 1158    if module_name is not None:1159        info = cm_global.error_dict.get(module_name)1160        if info is not None:1161            return web.json_response(info)1162 1163    return web.Response(status=400)1164 1165 1166@routes.post("/manager/queue/reinstall")1167async def reinstall_custom_node(request):1168    await uninstall_custom_node(request)1169    await install_custom_node(request)1170 1171 1172@routes.get("/manager/queue/reset")1173async def reset_queue(request):1174    global task_queue1175    task_queue = queue.Queue()1176    return web.Response(status=200)1177 1178 1179@routes.get("/manager/queue/status")1180async def queue_count(request):1181    global task_queue1182 1183    with task_worker_lock:1184        done_count = len(nodepack_result) + len(model_result)1185        in_progress_count = len(tasks_in_progress)1186        total_count = done_count + in_progress_count + task_queue.qsize()1187        is_processing = task_worker_thread is not None and task_worker_thread.is_alive()1188 1189    return web.json_response({1190        'total_count': total_count, 'done_count': done_count, 'in_progress_count': in_progress_count,1191        'is_processing': is_processing})1192 1193 1194@routes.post("/manager/queue/install")1195async def install_custom_node(request):1196    if not is_allowed_security_level('middle'):1197        logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)1198        return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")1199 1200    json_data = await request.json()

Showing the first 1,200 of 1766 lines. Download the file for the rest.