Compare commits

...

11 Commits

Author SHA1 Message Date
Dr.Lt.Data
3c2933338f fixed: /manager/queue/status - race condition issue 2025-02-02 10:38:05 +09:00
Robin Huang
829784fa50 Fix conditional for switch ComfyUI. 2025-02-01 16:16:37 -08:00
Robin Huang
3c45f8dc91 Hide update comfyui buttons in electron. 2025-02-01 16:15:27 -08:00
Dr.Lt.Data
f8ebf7c6ad update DB 2025-02-01 16:45:24 +09:00
Dr.Lt.Data
510c364607 feat: stop feature
feat: model-manager - support background tasking
2025-02-01 16:35:56 +09:00
Dr.Lt.Data
a3d6fcccb7 update DB 2025-02-01 14:01:18 +09:00
Dr.Lt.Data
42c8082edd fixed: duplicate endpoint function name 2025-02-01 11:39:48 +09:00
Dr.Lt.Data
1a7edf7f0e fixed: datetime.datetime crash - use hasattr instead of exception handling
https://github.com/ltdrdata/ComfyUI-Manager/issues/1503
2025-02-01 11:37:53 +09:00
Dr.Lt.Data
4760deaf9c feat: custom-nodes-manager - background tasks(install/update/fix/disable/enable) 2025-02-01 11:22:01 +09:00
Dr.Lt.Data
0f7b9d02a0 improved: more friendly log messages. 2025-01-31 21:42:26 +09:00
Dr.Lt.Data
adc86c7945 update DB 2025-01-31 21:36:24 +09:00
17 changed files with 3296 additions and 2834 deletions

View File

@@ -15468,7 +15468,7 @@
"https://github.com/SeanScripts/ComfyUI-PixtralLlamaMolmoVision"
],
"install_type": "git-clone",
"description": "For loading and running Pixtral, Llama 3.2 Vision, and Molmo models. Put models in the models/LLM folder.\n[w/Renamed from ComfyUI-PixtralLlamaVision. Please reinstall.]"
"description": "For loading and running Pixtral, Llama 3.2 Vision, and Molmo models. Put models in the models/LLM folder."
},
{
"author": "ExterminanzHS",
@@ -20251,7 +20251,7 @@
"https://github.com/willmiao/ComfyUI-Lora-Manager"
],
"install_type": "git-clone",
"description": "LoRA Manager for ComfyUI - An extension for managing LoRA models with previews and metadata integration."
"description": "LoRA Manager for ComfyUI - Access it at http://localhost:8188/loras for managing LoRA models with previews and metadata integration."
},
{
"author": "tigeryy2",
@@ -20364,6 +20364,27 @@
"install_type": "git-clone",
"description": "This node group contains a series of ComfyUI nodes with built-in counters and specific output results based on the counter's output, aimed at implementing folder traversal functionality in the ComfyUI frontend. For specific examples, please refer to the sample workflow. Of course, you can also use your imagination to create other interesting things."
},
{
"author": "agilly1989",
"title": "ComfyUI_agilly1989_motorway",
"reference": "https://github.com/agilly1989/ComfyUI_agilly1989_motorway",
"files": [
"https://github.com/agilly1989/ComfyUI_agilly1989_motorway"
],
"install_type": "git-clone",
"description": "This my implemenation of a `pipe` in ComfyUI. Is it better or worse than others? No idea."
},
{
"author": "myAiLemon",
"title": "MagicGetPromptAutomatically",
"reference": "https://github.com/myAiLemon/MagicGetPromptAutomatically",
"files": [
"https://github.com/myAiLemon/MagicGetPromptAutomatically"
],
"install_type": "git-clone",
"description": "A plug-in that can automatically generate pictures and save txt files in comfyui"
},

View File

@@ -2379,7 +2379,9 @@
"ClownsharKSamplerAutomation",
"ClownsharKSamplerAutomation_Beta",
"ClownsharKSamplerGuide",
"ClownsharKSamplerGuide_Beta",
"ClownsharKSamplerGuides",
"ClownsharKSamplerGuides_Beta",
"ClownsharKSamplerOptions",
"ClownsharKSamplerOptions_SDE_Noise",
"ClownsharkSamplerOptions_FrameWeights",
@@ -2816,11 +2818,17 @@
],
"https://github.com/DesertPixelAi/ComfyUI-Desert-Pixel-Nodes": [
[
"DP 10 String Switch",
"DP 10 Images Switch Or Batch",
"DP 10 String Switch Or Connect",
"DP 2 String Switch",
"DP 3 Images Switch Or Batch",
"DP 3 String Switch Or Connect",
"DP 5 Find And Replace",
"DP 5 String Switch",
"DP 5 Images Switch Or Batch",
"DP 5 String Switch Or Connect",
"DP Add Background To Png",
"DP Add Weight To String Sdxl",
"DP Advanced Sampler",
"DP Advanced Weight String Sdxl",
"DP Animation Calculator 10 Inputs",
"DP Animation Calculator 5 Inputs",
@@ -2830,8 +2838,7 @@
"DP Broken Token",
"DP Clean Prompt",
"DP Clean Prompt Travel",
"DP Combo Controller",
"DP Condition Mixer",
"DP Condition Switch",
"DP ControlNet Apply Advanced",
"DP Crazy Prompt Mixer",
"DP Create Json File",
@@ -2843,6 +2850,7 @@
"DP Fast Slow Motion",
"DP Five Lora",
"DP Five Lora Random",
"DP Float Stepper",
"DP Image And String Pairs Switch",
"DP Image Color Analyzer",
"DP Image Color Analyzer Small",
@@ -2853,12 +2861,10 @@
"DP Image Empty Latent Switch SDXL",
"DP Image Slide Show",
"DP Image Strip",
"DP Image Switch 10",
"DP Image Switch 3",
"DP Image Switch 5",
"DP Int 0-1000",
"DP Int 0-1000 4 Step",
"DP Int 0-1000 8 Step",
"DP Latent Split",
"DP Line Cycler",
"DP Load Checkpoint With Info",
"DP Load Controlnet Model With Name",
@@ -2868,13 +2874,10 @@
"DP Load Image Minimal",
"DP Load UNET With Info",
"DP Logo Animator",
"DP Logo Animator Advanced",
"DP Lora Random Strength Controller",
"DP Lora Strength Controller",
"DP Lora Strength Stepper",
"DP Mask Settings",
"DP Prompt Inverter",
"DP Prompt Manager",
"DP Prompt Manager Small",
"DP Prompt Mode Controller",
"DP Prompt Styler",
@@ -2892,9 +2895,7 @@
"DP Save Preview Image",
"DP Set New Model Folder Link",
"DP String Text",
"DP String Text With Weight",
"DP String With Switch",
"DP Strings Connector",
"DP String Text With Sdxl Weight",
"DP Strip Edge Masks",
"DP Switch Controller",
"DP Text Preview",
@@ -2905,10 +2906,8 @@
"DP Video Looper",
"DP Video Transition",
"DP_Crazy_Prompt_Mixer",
"DP_Logo_Animator_Advanced",
"DP_Lora_Strength_Stepper",
"DP_Prompt_Inverter",
"DP_Strings_Connector"
"DP_Float_Stepper",
"DP_Prompt_Inverter"
],
{
"title_aux": "ComfyUI-Desert-Pixel-Nodes"
@@ -4712,6 +4711,8 @@
],
"https://github.com/JTriggerFish/ComfyLatentTools": [
[
"DownsampledAttentionGuidance",
"DownsampledLatentGuidance",
"LatentNormalizedLanczosResize",
"RescaledPAG",
"RescaledSEG"
@@ -5988,7 +5989,8 @@
],
"https://github.com/Mattabyte/ComfyUI-SecureApiCall": [
[
"SecureAPI-SecureAPI"
"SecureAPI-SecureAPI",
"SecureAPI-SecureAPI-AWS"
],
{
"title_aux": "ComfyUI Secure API Call"
@@ -7348,6 +7350,7 @@
"AspectRatioAdvanced",
"DisplayEverything",
"ImageComparer",
"LensFlare",
"MultiFloat",
"MultiTextNode",
"SKB_AnySwitch",
@@ -8447,6 +8450,7 @@
"SD35StartSettings",
"SDXLStartSettings",
"SDstarsampler",
"Star Face Loader",
"StarFiveWildcards",
"StarImageSwitch",
"StarTextFilter",
@@ -10720,6 +10724,7 @@
],
"https://github.com/aidec/Comfyui_TextBatch_aidec": [
[
"ImageQueueProcessor",
"TextBatch",
"TextQueueProcessor",
"TextSplitCounter"
@@ -11877,18 +11882,22 @@
"SamplerSonarEuler",
"SamplerSonarEulerA",
"SonarAdvanced1fNoise",
"SonarAdvancedDistroNoise",
"SonarAdvancedPowerLawNoise",
"SonarAdvancedPyramidNoise",
"SonarBlendedNoise",
"SonarChannelNoise",
"SonarCompositeNoise",
"SonarCustomNoise",
"SonarCustomNoiseAdv",
"SonarGuidanceConfig",
"SonarGuidedNoise",
"SonarModulatedNoise",
"SonarRandomNoise",
"SonarRepeatedNoise",
"SonarScheduledNoise"
"SonarResizedNoise",
"SonarScheduledNoise",
"SonarWaveletFilteredNoise"
],
{
"title_aux": "ComfyUI-sonar"
@@ -12939,6 +12948,7 @@
"chaosaiart_controlnet_weidgth",
"chaosaiart_convert",
"chaosaiart_convert_Prompt",
"chaosaiart_deepseek_fix",
"chaosaiart_forPreview",
"chaosaiart_image_loop",
"chaosaiart_img2gif",
@@ -13892,6 +13902,7 @@
"PolyexponentialScheduler",
"PorterDuffImageComposite",
"Preview3D",
"Preview3DAnimation",
"PreviewAudio",
"PreviewImage",
"RandomNoise",
@@ -15705,6 +15716,7 @@
[
"FL_API_Base64_ImageLoader",
"FL_API_ImageSaver",
"FL_AnimeLineExtractor",
"FL_ApplyMask",
"FL_Ascii",
"FL_BatchAlign",
@@ -16632,8 +16644,10 @@
"Griptape Replace: Rulesets on Agent",
"Griptape Replace: Tools on Agent",
"Griptape Rerank Driver: Cohere",
"Griptape Retrieve: Cloud Ruleset",
"Griptape Run: Agent",
"Griptape Run: Audio Transcription",
"Griptape Run: Cloud Assistant",
"Griptape Run: Image Description",
"Griptape Run: Parallel Image Description",
"Griptape Run: Prompt Task",
@@ -22333,6 +22347,16 @@
"title_aux": "ComfyUI_KSamplerTimer"
}
],
"https://github.com/myAiLemon/MagicGetPromptAutomatically": [
[
"EditableStringNode",
"IntegratedCLIPTextEncodeWithExtract",
"ProcessAndSave"
],
{
"title_aux": "MagicGetPromptAutomatically"
}
],
"https://github.com/myshell-ai/ComfyUI-ShellAgent-Plugin": [
[
"ShellAgentPluginInputAudio",

View File

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ import manager_downloader
from node_package import InstalledNodePackage
version_code = [3, 11, 2]
version_code = [3, 13, 1]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')

View File

@@ -18,6 +18,8 @@ import manager_core as core
import manager_util
import cm_global
import logging
import asyncio
import queue
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
@@ -31,7 +33,6 @@ SECURITY_MESSAGE_GENERAL = "ERROR: This installation is not allowed in this secu
routes = PromptServer.instance.routes
def handle_stream(stream, prefix):
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
for msg in stream:
@@ -368,6 +369,204 @@ def nickname_filter(json_obj):
return json_obj
task_queue = queue.Queue()
nodepack_result = {}
model_result = {}
tasks_in_progress = set()
task_worker_lock = threading.Lock()
async def task_worker():
global task_queue
global nodepack_result
global model_result
global tasks_in_progress
async def do_install(item) -> str:
ui_id, node_spec_str, channel, mode, skip_post_install = item
try:
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
if node_spec is None:
logging.error(f"Cannot resolve install target: '{node_spec_str}'")
return f"Cannot resolve install target: '{node_spec_str}'"
node_name, version_spec, is_specified = node_spec
res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install)
# discard post install if skip_post_install mode
if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
return res.msg
elif not res.result:
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
return res.msg
return 'success'
except Exception:
traceback.print_exc()
return f"Installation failed:\n{node_spec_str}"
async def do_update(item) -> str:
ui_id, node_name, node_ver = item
try:
res = core.unified_manager.unified_update(node_name, node_ver)
manager_util.clear_pip_cache()
if res.result:
return 'success'
logging.error(f"\nERROR: An error occurred while updating '{node_name}'.")
except Exception:
traceback.print_exc()
return f"An error occurred while updating '{node_name}'."
async def do_fix(item) -> str:
ui_id, node_name, node_ver = item
try:
res = core.unified_manager.unified_fix(node_name, node_ver)
if res.result:
return 'success'
else:
logging.error(res.msg)
logging.error(f"\nERROR: An error occurred while fixing '{node_name}@{node_ver}'.")
except Exception:
traceback.print_exc()
return f"An error occurred while fixing '{node_name}@{node_ver}'."
async def do_uninstall(item) -> str:
ui_id, node_name, is_unknown = item
try:
res = core.unified_manager.unified_uninstall(node_name, is_unknown)
if res.result:
return 'success'
logging.error(f"\nERROR: An error occurred while uninstalling '{node_name}'.")
except Exception:
traceback.print_exc()
return f"An error occurred while uninstalling '{node_name}'."
async def do_disable(item) -> str:
ui_id, node_name, is_unknown = item
try:
res = core.unified_manager.unified_disable(node_name, is_unknown)
if res:
return 'success'
except Exception:
traceback.print_exc()
return f"Failed to disable: '{node_name}'"
async def do_install_model(item) -> str:
ui_id, json_data = item
model_path = get_model_path(json_data)
model_url = json_data['url']
res = False
try:
if model_path is not None:
logging.info(f"Install model '{json_data['name']}' from '{model_url}' into '{model_path}'")
if not core.get_config()['model_download_by_agent'] and (
model_url.startswith('https://github.com') or model_url.startswith('https://huggingface.co') or model_url.startswith('https://heibox.uni-heidelberg.de')):
model_dir = get_model_dir(json_data, True)
download_url(model_url, model_dir, filename=json_data['filename'])
if model_path.endswith('.zip'):
res = core.unzip(model_path)
else:
res = True
if res:
return 'success'
else:
res = download_url_with_agent(model_url, model_path)
if res and model_path.endswith('.zip'):
res = core.unzip(model_path)
else:
logging.error(f"Model installation error: invalid model type - {json_data['type']}")
if res:
return 'success'
except Exception as e:
logging.error(f"[ERROR] {e}", file=sys.stderr)
return f"Model installation error: {model_url}"
stats = {}
while True:
done_count = len(nodepack_result) + len(model_result)
total_count = done_count + task_queue.qsize()
if task_queue.empty():
logging.info(f"\n[ComfyUI-Manager] Queued works are completed.\n{stats}")
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
PromptServer.instance.send_sync("cm-queue-status",
{'status': 'done',
'nodepack_result': nodepack_result, 'model_result': model_result,
'total_count': total_count, 'done_count': done_count})
nodepack_result = {}
task_queue = queue.Queue()
return # terminate worker thread
with task_worker_lock:
kind, item = task_queue.get()
tasks_in_progress.add((kind, item[0]))
try:
if kind == 'install':
msg = await do_install(item)
elif kind == 'install-model':
msg = await do_install_model(item)
elif kind == 'update':
msg = await do_update(item)
elif kind == 'fix':
msg = await do_fix(item)
elif kind == 'uninstall':
msg = await do_uninstall(item)
elif kind == 'disable':
msg = await do_disable(item)
else:
msg = "Unexpected kind: " + kind
except Exception:
traceback.print_exc()
msg = f"Exception: {(kind, item)}"
with task_worker_lock:
tasks_in_progress.remove((kind, item[0]))
ui_id = item[0]
if kind == 'install-model':
model_result[ui_id] = msg
ui_target = "model_manager"
else:
nodepack_result[ui_id] = msg
ui_target = "nodepack_manager"
stats[kind] = stats.get(kind, 0) + 1
PromptServer.instance.send_sync("cm-queue-status",
{'status': 'in_progress', 'target': item[0], 'ui_target': ui_target,
'total_count': total_count, 'done_count': done_count})
@routes.get("/customnode/getmappings")
async def fetch_customnode_mappings(request):
"""
@@ -864,13 +1063,35 @@ async def import_fail_info(request):
return web.Response(status=400)
@routes.post("/customnode/reinstall")
@routes.post("/manager/queue/reinstall")
async def reinstall_custom_node(request):
await uninstall_custom_node(request)
await install_custom_node(request)
@routes.post("/customnode/install")
@routes.get("/manager/queue/reset")
async def reset_queue(request):
global task_queue
task_queue = queue.Queue()
return web.Response(status=200)
@routes.get("/manager/queue/status")
async def queue_count(request):
global task_queue
with task_worker_lock:
done_count = len(nodepack_result) + len(model_result)
in_progress_count = len(tasks_in_progress)
total_count = done_count + in_progress_count + task_queue.qsize()
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
return web.json_response({
'total_count': total_count, 'done_count': done_count, 'in_progress_count': in_progress_count,
'is_processing': is_processing})
@routes.post("/manager/queue/install")
async def install_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -913,26 +1134,33 @@ async def install_custom_node(request):
logging.error(SECURITY_MESSAGE_GENERAL)
return web.Response(status=404, text="A security error has occurred. Please check the terminal logs")
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install
task_queue.put(("install", install_item))
if node_spec is None:
return web.Response(status=400, text=f"Cannot resolve install target: '{node_spec_str}'")
node_name, version_spec, is_specified = node_spec
res = await core.unified_manager.install_by_id(node_name, version_spec, json_data['channel'], json_data['mode'], return_postinstall=skip_post_install)
# discard post install if skip_post_install mode
if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
return web.Response(status=400, text=res.msg)
elif not res.result:
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
return web.Response(status=400, text=res.msg)
return web.Response(status=200, text="Installation success.")
return web.Response(status=200)
@routes.post("/customnode/fix")
task_worker_thread:threading.Thread = None
@routes.get("/manager/queue/start")
async def queue_start(request):
global nodepack_result
global model_result
global task_worker_thread
if task_worker_thread is not None and task_worker_thread.is_alive():
return web.Response(status=201) # already in-progress
nodepack_result = {}
model_result = {}
task_worker_thread = threading.Thread(target=lambda: asyncio.run(task_worker()))
task_worker_thread.start()
return web.Response(status=200)
@routes.post("/manager/queue/fix")
async def fix_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_GENERAL)
@@ -948,16 +1176,10 @@ async def fix_custom_node(request):
# unknown
node_name = os.path.basename(json_data['files'][0])
res = core.unified_manager.unified_fix(node_name, node_ver)
update_item = json_data.get('ui_id'), node_name, json_data['version']
task_queue.put(("fix", update_item))
if res.result:
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
return web.json_response({}, content_type='application/json')
else:
logging.error(res.msg)
logging.error(f"\nERROR: An error occurred while fixing '{node_name}@{node_ver}'.")
return web.Response(status=400, text=f"An error occurred while fixing '{node_name}@{node_ver}'.")
return web.Response(status=200)
@routes.post("/customnode/install/git_url")
@@ -992,7 +1214,7 @@ async def install_custom_node_pip(request):
return web.Response(status=200)
@routes.post("/customnode/uninstall")
@routes.post("/manager/queue/uninstall")
async def uninstall_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1009,17 +1231,13 @@ async def uninstall_custom_node(request):
is_unknown = True
node_name = os.path.basename(json_data['files'][0])
res = core.unified_manager.unified_uninstall(node_name, is_unknown)
uninstall_item = json_data.get('ui_id'), node_name, is_unknown
task_queue.put(("uninstall", uninstall_item))
if res.result:
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
return web.json_response({}, content_type='application/json')
logging.error(f"\nERROR: An error occurred while uninstalling '{node_name}'.")
return web.Response(status=400, text=f"An error occurred while uninstalling '{node_name}'.")
return web.Response(status=200)
@routes.post("/customnode/update")
@routes.post("/manager/queue/update")
async def update_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1034,16 +1252,10 @@ async def update_custom_node(request):
# unknown
node_name = os.path.basename(json_data['files'][0])
res = core.unified_manager.unified_update(node_name, json_data['version'])
update_item = json_data.get('ui_id'), node_name, json_data['version']
task_queue.put(("update", update_item))
manager_util.clear_pip_cache()
if res.result:
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
return web.json_response({}, content_type='application/json')
logging.error(f"\nERROR: An error occurred while updating '{node_name}'.")
return web.Response(status=400, text=f"An error occurred while updating '{node_name}'.")
return web.Response(status=200)
@routes.get("/comfyui_manager/update_comfyui")
@@ -1057,8 +1269,10 @@ async def update_comfyui(request):
logging.error("ComfyUI update fail: The installed ComfyUI does not have a Git repository.")
return web.Response(status=400)
elif res == "updated":
logging.info("ComfyUI is updated.")
return web.Response(status=201)
else: # skipped
logging.info("ComfyUI is up-to-date.")
return web.Response(status=200)
except Exception as e:
logging.error(f"ComfyUI update fail: {e}", file=sys.stderr)
@@ -1090,7 +1304,7 @@ async def comfyui_switch_version(request):
return web.Response(status=400)
@routes.post("/customnode/disable")
@routes.post("/manager/queue/disable")
async def disable_node(request):
json_data = await request.json()
@@ -1103,12 +1317,10 @@ async def disable_node(request):
is_unknown = True
node_name = os.path.basename(json_data['files'][0])
res = core.unified_manager.unified_disable(node_name, is_unknown)
update_item = json_data.get('ui_id'), node_name, is_unknown
task_queue.put(("disable", update_item))
if res:
return web.json_response({}, content_type='application/json')
return web.Response(status=400, text="Failed to disable")
return web.Response(status=200)
@routes.get("/manager/migrate_unmanaged_nodes")
@@ -1124,12 +1336,10 @@ async def need_to_migrate(request):
return web.Response(text=str(core.need_to_migrate), status=200)
@routes.post("/model/install")
@routes.post("/manager/queue/install_model")
async def install_model(request):
json_data = await request.json()
model_path = get_model_path(json_data)
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
return web.Response(status=403)
@@ -1147,37 +1357,10 @@ async def install_model(request):
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
return web.Response(status=403)
res = False
install_item = json_data.get('ui_id'), json_data
task_queue.put(("install-model", install_item))
try:
if model_path is not None:
model_url = json_data['url']
logging.info(f"Install model '{json_data['name']}' from '{model_url}' into '{model_path}'")
if not core.get_config()['model_download_by_agent'] and (
model_url.startswith('https://github.com') or model_url.startswith('https://huggingface.co') or model_url.startswith('https://heibox.uni-heidelberg.de')):
model_dir = get_model_dir(json_data, True)
download_url(model_url, model_dir, filename=json_data['filename'])
if model_path.endswith('.zip'):
res = core.unzip(model_path)
else:
res = True
if res:
return web.json_response({}, content_type='application/json')
else:
res = download_url_with_agent(model_url, model_path)
if res and model_path.endswith('.zip'):
res = core.unzip(model_path)
else:
logging.error(f"Model installation error: invalid model type - {json_data['type']}")
if res:
return web.json_response({}, content_type='application/json')
except Exception as e:
logging.error(f"[ERROR] {e}", file=sys.stderr)
return web.Response(status=400)
return web.Response(status=200)
@routes.get("/manager/preview_method")
@@ -1406,8 +1589,6 @@ def confirm_try_install(sender, custom_node_url, msg):
cm_global.register_api('cm.try-install-custom-node', confirm_try_install)
import asyncio
async def default_cache_update():
async def get_cache(filename):
@@ -1434,6 +1615,8 @@ async def default_cache_update():
await core.unified_manager.reload('remote', dont_wait=False)
await core.unified_manager.get_custom_nodes('default', 'remote')
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")
# NOTE: hide migration button temporarily.
# if not core.get_config()['skip_migration_check']:
# await core.check_need_to_migrate()

View File

@@ -17,7 +17,6 @@ import {
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
import { CustomNodesManager } from "./custom-nodes-manager.js";
import { ModelManager } from "./model-manager.js";
import { set_double_click_policy } from "./node_fixer.js";
import { SnapshotManager } from "./snapshot.js";
var docStyle = document.createElement('style');
@@ -812,11 +811,15 @@ const isOutputNode = (node) => {
class ManagerMenuDialog extends ComfyDialog {
createControlsMid() {
let self = this;
const isElectron = 'electronAPI' in window;
update_comfyui_button =
$el("button.cm-button", {
type: "button",
textContent: "Update ComfyUI",
style: {
display: isElectron ? 'none' : 'block'
},
onclick:
() => updateComfyUI()
});
@@ -825,6 +828,9 @@ class ManagerMenuDialog extends ComfyDialog {
$el("button.cm-button", {
type: "button",
textContent: "Switch ComfyUI",
style: {
display: isElectron ? 'none' : 'block'
},
onclick:
() => switchComfyUI()
});
@@ -1607,4 +1613,4 @@ async function set_default_ui()
}
}
set_default_ui();
set_default_ui();

View File

@@ -130,6 +130,20 @@ export function customAlert(message) {
}
}
export function infoToast(summary, message) {
try {
app.extensionManager.toast.add({
severity: 'info',
summary: summary,
detail: message,
life: 3000
})
}
catch {
// do nothing
}
}
export async function customPrompt(title, message) {
try {

View File

@@ -4,7 +4,7 @@ import { api } from "../../scripts/api.js";
import {
manager_instance, rebootAPI, install_via_git_url,
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt, sanitizeHTML
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt, sanitizeHTML, infoToast
} from "./common.js";
// https://cenfun.github.io/turbogrid/api.html
@@ -55,6 +55,12 @@ const pageCss = `
color: white;
}
.cn-manager .cn-manager-stop {
display: none;
background-color: #500000;
color: white;
}
.cn-manager .cn-manager-back {
align-items: center;
justify-content: center;
@@ -344,13 +350,14 @@ const pageHtml = `
<div class="cn-manager-selection"></div>
<div class="cn-manager-message"></div>
<div class="cn-manager-footer">
<button class="cn-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cn-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cn-manager-restart">Restart</button>
<button class="cn-manager-stop">Stop</button>
<div class="cn-flex-auto"></div>
<button class="cn-manager-check-update">Check Update</button>
<button class="cn-manager-check-missing">Check Missing</button>
@@ -391,6 +398,8 @@ export class CustomNodesManager {
this.restartMap = {};
this.init();
api.addEventListener("cm-queue-status", this.onQueueStatus);
}
init() {
@@ -760,6 +769,13 @@ export class CustomNodesManager {
}
},
".cn-manager-stop": {
click: () => {
api.fetchApi('/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
".cn-manager-check-update": {
click: (e) => {
e.target.classList.add("cn-btn-loading");
@@ -1204,7 +1220,7 @@ export class CustomNodesManager {
}
focusInstall(item, mode) {
const cellNode = this.grid.getCellNode(item, "installed");
const cellNode = this.grid.getCellNode(item, "action");
if (cellNode) {
const cellBtn = cellNode.querySelector(`button[mode="${mode}"]`);
if (cellBtn) {
@@ -1269,6 +1285,13 @@ export class CustomNodesManager {
}
async installNodes(list, btn, title, selected_version) {
let stats = await api.fetchApi('/manager/queue/status');
stats = await stats.json();
if(stats.is_processing) {
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
return;
}
const { target, label, mode} = btn;
if(mode === "uninstall") {
@@ -1294,8 +1317,15 @@ export class CustomNodesManager {
let needRestart = false;
let errorMsg = "";
await api.fetchApi('/manager/queue/reset');
let target_items = [];
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash);
target_items.push(item);
if (!item) {
errorMsg = `Not found custom node: ${hash}`;
break;
@@ -1315,6 +1345,7 @@ export class CustomNodesManager {
data.selected_version = selected_version;
data.channel = this.channel;
data.mode = this.mode;
data.ui_id = hash;
let install_mode = mode;
if(mode == 'switch') {
@@ -1332,14 +1363,14 @@ export class CustomNodesManager {
api_mode = 'reinstall';
}
const res = await api.fetchApi(`/customnode/${api_mode}`, {
const res = await api.fetchApi(`/manager/queue/${api_mode}`, {
method: 'POST',
body: JSON.stringify(data)
});
if (res.status != 200) {
errorMsg = `${item.title} ${mode} failed: `;
if(res.status == 403) {
errorMsg += `This action is not allowed with this security level configuration.`;
} else if(res.status == 404) {
@@ -1350,32 +1381,91 @@ export class CustomNodesManager {
break;
}
needRestart = true;
this.grid.setRowSelected(item, false);
item.restart = true;
this.restartMap[item.hash] = true;
this.grid.updateCell(item, "action");
//console.log(res.data);
}
target.classList.remove("cn-btn-loading");
this.install_context = {btn: btn, targets: target_items};
if (errorMsg) {
for(let k in target_items) {
let item = this.install_context.targets[k];
this.grid.updateCell(item, "action");
}
if(errorMsg) {
this.showError(errorMsg);
show_message("Installation Error:\n"+errorMsg);
// reset
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash);
self.grid.updateCell(item, "action");
}
}
else {
await api.fetchApi('/manager/queue/start');
this.showStop();
}
}
async onQueueStatus(event) {
let self = CustomNodesManager.instance;
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') {
const hash = event.detail.target;
const item = self.grid.getRowItemBy("hash", hash);
item.restart = true;
self.restartMap[item.hash] = true;
self.grid.updateCell(item, "action");
self.grid.setRowSelected(item, false);
}
else if(event.detail.status == 'done') {
self.hideStop();
self.onQueueCompleted(event.detail);
}
}
async onQueueCompleted(info) {
let result = info.nodepack_result;
if(result.length == 0) {
return;
}
let self = CustomNodesManager.instance;
if(!self.install_context) {
return;
}
const { target, label, mode } = self.install_context.btn;
target.classList.remove("cn-btn-loading");
let errorMsg = "";
for(let hash in result){
let v = result[hash];
if(v != 'success')
errorMsg += v;
}
for(let k in self.install_context.targets) {
let item = self.install_context.targets[k];
self.grid.updateCell(item, "action");
}
if (errorMsg) {
self.showError(errorMsg);
show_message("Installation Error:\n"+errorMsg);
} else {
this.showStatus(`${label} ${list.length} custom node(s) successfully`);
self.showStatus(`${label} ${result.length} custom node(s) successfully`);
}
if (needRestart) {
this.showRestart();
this.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red")
}
self.showRestart();
self.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red");
infoToast(`[ComfyUI-Manager] All node pack tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
self.install_context = undefined;
}
// ===========================================================================================
@@ -1756,9 +1846,9 @@ export class CustomNodesManager {
}
setDisabled(disabled) {
const $close = this.element.querySelector(".cn-manager-close");
const $restart = this.element.querySelector(".cn-manager-restart");
const $stop = this.element.querySelector(".cn-manager-stop");
const list = [
".cn-manager-header input",
@@ -1770,7 +1860,7 @@ export class CustomNodesManager {
})
.flat()
.filter(it => {
return it !== $close && it !== $restart;
return it !== $close && it !== $restart && it !== $stop;
});
list.forEach($elem => {
@@ -1791,6 +1881,14 @@ export class CustomNodesManager {
this.element.querySelector(".cn-manager-restart").style.display = "block";
}
showStop() {
this.element.querySelector(".cn-manager-stop").style.display = "block";
}
hideStop() {
this.element.querySelector(".cn-manager-stop").style.display = "none";
}
setFilter(filterValue) {
let filter = "";
const filterItem = this.getFilterItem(filterValue);

View File

@@ -1,8 +1,10 @@
import { app } from "../../scripts/app.js";
import { $el } from "../../scripts/ui.js";
import {
manager_instance, rebootAPI,
fetchData, md5, icons
fetchData, md5, icons, show_message, customAlert, infoToast
} from "./common.js";
import { api } from "../../scripts/api.js";
// https://cenfun.github.io/turbogrid/api.html
import TG from "./turbogrid.esm.js";
@@ -46,6 +48,18 @@ const pageCss = `
background-color: var(--comfy-input-bg);
}
.cmm-manager .cmm-manager-refresh {
display: none;
background-color: #000080;
color: white;
}
.cmm-manager .cmm-manager-stop {
display: none;
background-color: #500000;
color: white;
}
.cmm-manager-header {
display: flex;
flex-wrap: wrap;
@@ -235,7 +249,14 @@ const pageHtml = `
<div class="cmm-manager-selection"></div>
<div class="cmm-manager-message"></div>
<div class="cmm-manager-footer">
<button class="cmm-manager-back">Back</button>
<button class="cmm-manager-back">
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Back
</button>
<button class="cmm-manager-refresh">Refresh</button>
<button class="cmm-manager-stop">Stop</button>
<div class="cmm-flex-auto"></div>
</div>
`;
@@ -254,6 +275,8 @@ export class ModelManager {
this.keywords = '';
this.init();
api.addEventListener("cm-queue-status", this.onQueueStatus);
}
init() {
@@ -365,12 +388,25 @@ export class ModelManager {
}
},
".cmm-manager-refresh": {
click: () => {
app.refreshComboInNodes();
}
},
".cmm-manager-stop": {
click: () => {
api.fetchApi('/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
".cmm-manager-back": {
click: (e) => {
this.close()
manager_instance.show();
}
},
}
};
Object.keys(eventsMap).forEach(selector => {
const target = this.element.querySelector(selector);
@@ -595,17 +631,28 @@ export class ModelManager {
}
async installModels(list, btn) {
let stats = await api.fetchApi('/manager/queue/status');
stats = await stats.json();
if(stats.is_processing) {
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
return;
}
btn.classList.add("cmm-btn-loading");
this.showLoading();
this.showError("");
let needRestart = false;
let needRefresh = false;
let errorMsg = "";
await api.fetchApi('/manager/queue/reset');
let target_items = [];
for (const item of list) {
this.grid.scrollRowIntoView(item);
target_items.push(item);
if (!this.focusInstall(item)) {
this.grid.onNextUpdated(() => {
@@ -616,48 +663,104 @@ export class ModelManager {
this.showStatus(`Install ${item.name} ...`);
const data = item.originalData;
const res = await fetchData('/model/install', {
data.ui_id = item.hash;
const res = await api.fetchApi(`/manager/queue/install_model`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
if (res.error) {
if (res.status != 200) {
errorMsg = `Install failed: ${item.name} ${res.error.message}`;
break;;
break;
}
}
needRestart = true;
this.install_context = {btn: btn, targets: target_items};
this.grid.setRowSelected(item, false);
if(errorMsg) {
this.showError(errorMsg);
show_message("Installation Error:\n"+errorMsg);
// reset
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash);
this.grid.updateCell(item, "installed");
}
}
else {
await api.fetchApi('/manager/queue/start');
this.showStop();
}
}
async onQueueStatus(event) {
let self = ModelManager.instance;
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'model_manager') {
const hash = event.detail.target;
const item = self.grid.getRowItemBy("hash", hash);
item.refresh = true;
self.grid.setRowSelected(item, false);
item.selectable = false;
this.grid.updateCell(item, "installed");
this.grid.updateCell(item, "tg-column-select");
// self.grid.updateCell(item, "tg-column-select");
self.grid.updateRow(item);
}
else if(event.detail.status == 'done') {
self.hideStop();
self.onQueueCompleted(event.detail);
}
}
this.showStatus(`Install ${item.name} successfully`);
async onQueueCompleted(info) {
let result = info.model_result;
if(result.length == 0) {
return;
}
this.hideLoading();
let self = ModelManager.instance;
if(!self.install_context) {
return;
}
let btn = self.install_context.btn;
self.hideLoading();
btn.classList.remove("cmm-btn-loading");
let errorMsg = "";
for(let hash in result){
let v = result[hash];
if(v != 'success')
errorMsg += v;
}
for(let k in self.install_context.targets) {
let item = self.install_context.targets[k];
self.grid.updateCell(item, "installed");
}
if (errorMsg) {
this.showError(errorMsg);
self.showError(errorMsg);
show_message("Installation Error:\n"+errorMsg);
} else {
this.showStatus(`Install ${list.length} models successfully`);
self.showStatus(`Install ${result.length} models successfully`);
}
if (needRestart) {
this.showMessage(`To apply the installed model, please click the 'Refresh' button on the main menu.`, "red")
}
self.showRefresh();
self.showMessage(`To apply the installed model, please click the 'Refresh' button.`, "red")
infoToast('Tasks done', `[ComfyUI-Manager] All model downloading tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
self.install_context = undefined;
}
getModelList(models) {
const typeMap = new Map();
const baseMap = new Map();
@@ -826,7 +929,7 @@ export class ModelManager {
}
showLoading() {
this.setDisabled(true);
// this.setDisabled(true);
if (this.grid) {
this.grid.showLoading();
this.grid.showMask({
@@ -836,7 +939,7 @@ export class ModelManager {
}
hideLoading() {
this.setDisabled(false);
// this.setDisabled(false);
if (this.grid) {
this.grid.hideLoading();
this.grid.hideMask();
@@ -844,8 +947,9 @@ export class ModelManager {
}
setDisabled(disabled) {
const $close = this.element.querySelector(".cmm-manager-close");
const $refresh = this.element.querySelector(".cmm-manager-refresh");
const $stop = this.element.querySelector(".cmm-manager-stop");
const list = [
".cmm-manager-header input",
@@ -857,7 +961,7 @@ export class ModelManager {
})
.flat()
.filter(it => {
return it !== $close;
return it !== $close && it !== $refresh && it !== $stop;
});
list.forEach($elem => {
@@ -874,6 +978,18 @@ export class ModelManager {
}
showRefresh() {
this.element.querySelector(".cmm-manager-refresh").style.display = "block";
}
showStop() {
this.element.querySelector(".cmm-manager-stop").style.display = "block";
}
hideStop() {
this.element.querySelector(".cmm-manager-stop").style.display = "none";
}
setKeywords(keywords = "") {
this.keywords = keywords;
this.element.querySelector(".cmm-manager-keywords").value = keywords;

View File

@@ -1,16 +1,6 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
let double_click_policy = "copy-all";
api.fetchApi('/manager/dbl_click/policy')
.then(response => response.text())
.then(data => set_double_click_policy(data));
export function set_double_click_policy(mode) {
double_click_policy = mode;
}
function addMenuHandler(nodeType, cb) {
const getOpts = nodeType.prototype.getExtraMenuOptions;
nodeType.prototype.getExtraMenuOptions = function () {
@@ -153,62 +143,6 @@ function node_info_copy(src, dest, connect_both, copy_shape) {
app.registerExtension({
name: "Comfy.Manager.NodeFixer",
async nodeCreated(node, app) {
let orig_dblClick = node.onDblClick;
node.onDblClick = function (e, pos, self) {
orig_dblClick?.apply?.(this, arguments);
if((!node.inputs && !node.outputs) || pos[1] > 0)
return;
switch(double_click_policy) {
case "copy-all":
case "copy-full":
case "copy-input":
{
if(node.inputs?.some(x => x.link != null) || node.outputs?.some(x => x.links != null && x.links.length > 0) )
return;
let src_node = lookup_nearest_nodes(node);
if(src_node)
{
let both_connection = double_click_policy != "copy-input";
let copy_shape = double_click_policy == "copy-full";
node_info_copy(src_node, node, both_connection, copy_shape);
}
}
break;
case "possible-input":
{
let nearest_inputs = lookup_nearest_inputs(node);
if(nearest_inputs)
connect_inputs(nearest_inputs, node);
}
break;
case "dual":
{
if(pos[0] < node.size[0]/2) {
// left: possible-input
let nearest_inputs = lookup_nearest_inputs(node);
if(nearest_inputs)
connect_inputs(nearest_inputs, node);
}
else {
// right: copy-all
if(node.inputs?.some(x => x.link != null) || node.outputs?.some(x => x.links != null && x.links.length > 0) )
return;
let src_node = lookup_nearest_nodes(node);
if(src_node)
node_info_copy(src_node, node, true);
}
}
break;
}
}
},
beforeRegisterNodeDef(nodeType, nodeData, app) {
addMenuHandler(nodeType, function (_, options) {
options.push({

View File

@@ -11,6 +11,16 @@
{
"author": "neverbiasu",
"title": "ComfyUI-DeepSeek",
"reference": "https://github.com/neverbiasu/ComfyUI-DeepSeek",
"files": [
"https://github.com/neverbiasu/ComfyUI-DeepSeek"
],
"install_type": "git-clone",
"description": "NODES: DeepSeek Caller"
},
{
"author": "Krish-701",
"title": "RK_Comfyui",

View File

@@ -924,6 +924,7 @@
"DeepSeekImageGeneration",
"DeepSeekImageUnderstanding",
"DeepSeekModelLoader",
"LLM_Loader",
"OpenAICompatibleLoader"
],
{
@@ -1628,6 +1629,8 @@
"FrameBlend",
"ImageReferenceUpdate",
"ImageSelector",
"KeypointsInputNode",
"KeypointsToPoseNode",
"SimHashCompare",
"TemporalConsistency"
],
@@ -2438,6 +2441,7 @@
"PolyexponentialScheduler",
"PorterDuffImageComposite",
"Preview3D",
"Preview3DAnimation",
"PreviewAudio",
"PreviewImage",
"RandomNoise",
@@ -3317,12 +3321,16 @@
[
"ForceMinimumBatchSize",
"ImageCompositeChained",
"LineDetection",
"MangaPanelSegmentationNode",
"Mask_Fill_Region",
"MatchImageCountToMaskCount",
"ParallaxGPUTest",
"ParallaxTest",
"RandomCharacterPrompts",
"TargetLocationCrop",
"TargetLocationPaste",
"Yolov8Detection",
"easy_parallax",
"string_list_to_prompt_schedule"
],
@@ -4169,6 +4177,14 @@
"title_aux": "ComfyUI-ControlNeXt [WIP]"
}
],
"https://github.com/neverbiasu/ComfyUI-DeepSeek": [
[
"DeepSeekCaller"
],
{
"title_aux": "ComfyUI-DeepSeek"
}
],
"https://github.com/neverbiasu/ComfyUI-StereoCrafter": [
[
"DepthSplattingModelLoader",
@@ -4237,7 +4253,8 @@
],
"https://github.com/nomcycle/ComfyUI_Cluster": [
[
"FenceClusteredWorkflow"
"ClusterFanInTensorsToBatch",
"ClusterInstanceIndex"
],
{
"title_aux": "ComfyUI_Cluster [WIP]"

View File

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,26 @@
},
{
"author": "agilly1989",
"title": "ComfyUI_agilly1989_motorway",
"reference": "https://github.com/agilly1989/ComfyUI_agilly1989_motorway",
"files": [
"https://github.com/agilly1989/ComfyUI_agilly1989_motorway"
],
"install_type": "git-clone",
"description": "This my implemenation of a `pipe` in ComfyUI. Is it better or worse than others? No idea."
},
{
"author": "myAiLemon",
"title": "MagicGetPromptAutomatically",
"reference": "https://github.com/myAiLemon/MagicGetPromptAutomatically",
"files": [
"https://github.com/myAiLemon/MagicGetPromptAutomatically"
],
"install_type": "git-clone",
"description": "A plug-in that can automatically generate pictures and save txt files in comfyui"
},
{
"author": "kijai",
"title": "ComfyUI-StableXWrapper",
@@ -667,26 +687,6 @@
],
"install_type": "git-clone",
"description": "A Text To Speech node using Kokoro TTS in ComfyUI."
},
{
"author": "WangPengxing",
"title": "ComfyUI WPX Nodes",
"reference": "https://github.com/WangPengxing/ComfyUI_WPX_Node",
"files": [
"https://github.com/WangPengxing/ComfyUI_WPX_Node"
],
"install_type": "git-clone",
"description": "A custom node collection for ComfyUI, offering enhanced image processing features."
},
{
"author": "PixelFunAI",
"title": "Hunyuan LoRA Loader Nodes",
"reference": "https://github.com/PixelFunAI/ComfyUI_PixelFun",
"files": [
"https://github.com/PixelFunAI/ComfyUI_PixelFun"
],
"install_type": "git-clone",
"description": "This collection provides four additional nodes for loading and managing Hunyuan Video LoRAs in ComfyUI"
}
]
}

View File

@@ -2379,7 +2379,9 @@
"ClownsharKSamplerAutomation",
"ClownsharKSamplerAutomation_Beta",
"ClownsharKSamplerGuide",
"ClownsharKSamplerGuide_Beta",
"ClownsharKSamplerGuides",
"ClownsharKSamplerGuides_Beta",
"ClownsharKSamplerOptions",
"ClownsharKSamplerOptions_SDE_Noise",
"ClownsharkSamplerOptions_FrameWeights",
@@ -2816,11 +2818,17 @@
],
"https://github.com/DesertPixelAi/ComfyUI-Desert-Pixel-Nodes": [
[
"DP 10 String Switch",
"DP 10 Images Switch Or Batch",
"DP 10 String Switch Or Connect",
"DP 2 String Switch",
"DP 3 Images Switch Or Batch",
"DP 3 String Switch Or Connect",
"DP 5 Find And Replace",
"DP 5 String Switch",
"DP 5 Images Switch Or Batch",
"DP 5 String Switch Or Connect",
"DP Add Background To Png",
"DP Add Weight To String Sdxl",
"DP Advanced Sampler",
"DP Advanced Weight String Sdxl",
"DP Animation Calculator 10 Inputs",
"DP Animation Calculator 5 Inputs",
@@ -2830,8 +2838,7 @@
"DP Broken Token",
"DP Clean Prompt",
"DP Clean Prompt Travel",
"DP Combo Controller",
"DP Condition Mixer",
"DP Condition Switch",
"DP ControlNet Apply Advanced",
"DP Crazy Prompt Mixer",
"DP Create Json File",
@@ -2843,6 +2850,7 @@
"DP Fast Slow Motion",
"DP Five Lora",
"DP Five Lora Random",
"DP Float Stepper",
"DP Image And String Pairs Switch",
"DP Image Color Analyzer",
"DP Image Color Analyzer Small",
@@ -2853,12 +2861,10 @@
"DP Image Empty Latent Switch SDXL",
"DP Image Slide Show",
"DP Image Strip",
"DP Image Switch 10",
"DP Image Switch 3",
"DP Image Switch 5",
"DP Int 0-1000",
"DP Int 0-1000 4 Step",
"DP Int 0-1000 8 Step",
"DP Latent Split",
"DP Line Cycler",
"DP Load Checkpoint With Info",
"DP Load Controlnet Model With Name",
@@ -2868,13 +2874,10 @@
"DP Load Image Minimal",
"DP Load UNET With Info",
"DP Logo Animator",
"DP Logo Animator Advanced",
"DP Lora Random Strength Controller",
"DP Lora Strength Controller",
"DP Lora Strength Stepper",
"DP Mask Settings",
"DP Prompt Inverter",
"DP Prompt Manager",
"DP Prompt Manager Small",
"DP Prompt Mode Controller",
"DP Prompt Styler",
@@ -2892,9 +2895,7 @@
"DP Save Preview Image",
"DP Set New Model Folder Link",
"DP String Text",
"DP String Text With Weight",
"DP String With Switch",
"DP Strings Connector",
"DP String Text With Sdxl Weight",
"DP Strip Edge Masks",
"DP Switch Controller",
"DP Text Preview",
@@ -2905,10 +2906,8 @@
"DP Video Looper",
"DP Video Transition",
"DP_Crazy_Prompt_Mixer",
"DP_Logo_Animator_Advanced",
"DP_Lora_Strength_Stepper",
"DP_Prompt_Inverter",
"DP_Strings_Connector"
"DP_Float_Stepper",
"DP_Prompt_Inverter"
],
{
"title_aux": "ComfyUI-Desert-Pixel-Nodes"
@@ -4712,6 +4711,8 @@
],
"https://github.com/JTriggerFish/ComfyLatentTools": [
[
"DownsampledAttentionGuidance",
"DownsampledLatentGuidance",
"LatentNormalizedLanczosResize",
"RescaledPAG",
"RescaledSEG"
@@ -5988,7 +5989,8 @@
],
"https://github.com/Mattabyte/ComfyUI-SecureApiCall": [
[
"SecureAPI-SecureAPI"
"SecureAPI-SecureAPI",
"SecureAPI-SecureAPI-AWS"
],
{
"title_aux": "ComfyUI Secure API Call"
@@ -7348,6 +7350,7 @@
"AspectRatioAdvanced",
"DisplayEverything",
"ImageComparer",
"LensFlare",
"MultiFloat",
"MultiTextNode",
"SKB_AnySwitch",
@@ -8447,6 +8450,7 @@
"SD35StartSettings",
"SDXLStartSettings",
"SDstarsampler",
"Star Face Loader",
"StarFiveWildcards",
"StarImageSwitch",
"StarTextFilter",
@@ -10720,6 +10724,7 @@
],
"https://github.com/aidec/Comfyui_TextBatch_aidec": [
[
"ImageQueueProcessor",
"TextBatch",
"TextQueueProcessor",
"TextSplitCounter"
@@ -11877,18 +11882,22 @@
"SamplerSonarEuler",
"SamplerSonarEulerA",
"SonarAdvanced1fNoise",
"SonarAdvancedDistroNoise",
"SonarAdvancedPowerLawNoise",
"SonarAdvancedPyramidNoise",
"SonarBlendedNoise",
"SonarChannelNoise",
"SonarCompositeNoise",
"SonarCustomNoise",
"SonarCustomNoiseAdv",
"SonarGuidanceConfig",
"SonarGuidedNoise",
"SonarModulatedNoise",
"SonarRandomNoise",
"SonarRepeatedNoise",
"SonarScheduledNoise"
"SonarResizedNoise",
"SonarScheduledNoise",
"SonarWaveletFilteredNoise"
],
{
"title_aux": "ComfyUI-sonar"
@@ -12939,6 +12948,7 @@
"chaosaiart_controlnet_weidgth",
"chaosaiart_convert",
"chaosaiart_convert_Prompt",
"chaosaiart_deepseek_fix",
"chaosaiart_forPreview",
"chaosaiart_image_loop",
"chaosaiart_img2gif",
@@ -13892,6 +13902,7 @@
"PolyexponentialScheduler",
"PorterDuffImageComposite",
"Preview3D",
"Preview3DAnimation",
"PreviewAudio",
"PreviewImage",
"RandomNoise",
@@ -15705,6 +15716,7 @@
[
"FL_API_Base64_ImageLoader",
"FL_API_ImageSaver",
"FL_AnimeLineExtractor",
"FL_ApplyMask",
"FL_Ascii",
"FL_BatchAlign",
@@ -16632,8 +16644,10 @@
"Griptape Replace: Rulesets on Agent",
"Griptape Replace: Tools on Agent",
"Griptape Rerank Driver: Cohere",
"Griptape Retrieve: Cloud Ruleset",
"Griptape Run: Agent",
"Griptape Run: Audio Transcription",
"Griptape Run: Cloud Assistant",
"Griptape Run: Image Description",
"Griptape Run: Parallel Image Description",
"Griptape Run: Prompt Task",
@@ -22333,6 +22347,16 @@
"title_aux": "ComfyUI_KSamplerTimer"
}
],
"https://github.com/myAiLemon/MagicGetPromptAutomatically": [
[
"EditableStringNode",
"IntegratedCLIPTextEncodeWithExtract",
"ProcessAndSave"
],
{
"title_aux": "MagicGetPromptAutomatically"
}
],
"https://github.com/myshell-ai/ComfyUI-ShellAgent-Plugin": [
[
"ShellAgentPluginInputAudio",

View File

@@ -20,13 +20,13 @@ import cm_global
import manager_downloader
import folder_paths
try:
from datetime import datetime
import datetime
if hasattr(datetime, 'datetime'):
def current_timestamp():
return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
except:
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
else:
# NOTE: Occurs in some Mac environments.
import time
import datetime
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{datetime.__file__}'")
def current_timestamp():
return str(time.time()).split('.')[0]

View File

@@ -1,7 +1,7 @@
[project]
name = "comfyui-manager"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
version = "3.11.2"
version = "3.13.1"
license = { file = "LICENSE.txt" }
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]