Compare commits

...

3 Commits

Author SHA1 Message Date
Juggernaut
a2d9d07ab4
Merge af1c698117 into 74d1e9d296 2026-01-19 16:07:17 +05:30
Dr.Lt.Data
74d1e9d296 update DB
Some checks failed
Python Linting / Run Ruff (push) Has been cancelled
2026-01-19 19:34:31 +09:00
ashish
af1c698117 dependency analysis 2026-01-18 16:54:22 +05:30
11 changed files with 3601 additions and 2230 deletions

View File

@ -24136,6 +24136,16 @@
"install_type": "git-clone",
"description": "ComfyUI-TinyBreaker is a collection of custom nodes specifically designed to generate images using the TinyBreaker model. It's actively developed with ongoing improvements. Although still in progress, these nodes are functional and allow you to explore the potential of the model."
},
{
"author": "martin-rizzo",
"title": "ComfyUI-ZImagePowerNodes",
"reference": "https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes",
"files": [
"https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes"
],
"install_type": "git-clone",
"description": "A set of ComfyUI nodes designed and fine-tuned specifically for the Z-Image model. Pushing the best image generation model to its limits!"
},
{
"author": "Arkanun",
"title": "ReadCSV_ComfyUI",
@ -40636,6 +40646,16 @@
"install_type": "git-clone",
"description": "A visual dashboard for managing, versioning, and batch-processing JSON configuration files for AI video generation workflows with Streamlit web interface and ComfyUI custom nodes."
},
{
"author": "ethanfel",
"title": "ComfyUI-Sharp-Selector",
"reference": "https://github.com/ethanfel/ComfyUI-Sharp-Selector",
"files": [
"https://github.com/ethanfel/ComfyUI-Sharp-Selector"
],
"install_type": "git-clone",
"description": "Custom nodes for sharp frame selection and analysis. (Description by CC)"
},
{
"author": "shin131002",
"title": "RandomLoRALoader",
@ -41338,6 +41358,16 @@
"install_type": "git-clone",
"description": "Provide a simple interface to simplify prompt writing."
},
{
"author": "0nikod",
"title": "ComfyUI-Metadata-Tools",
"reference": "https://github.com/0nikod/ComfyUI-Metadata-Tools",
"files": [
"https://github.com/0nikod/ComfyUI-Metadata-Tools"
],
"install_type": "git-clone",
"description": "Tools about metadata."
},
{
"author": "Suzu008",
"title": "ComfyUI-CryptIO",
@ -41670,7 +41700,46 @@
"install_type": "git-clone",
"description": "Comprehensive single-node solution for ComfyUI integrating base generation, latent refinement, neural upscaling, and post-processing."
},
{
"author": "ato-zen",
"title": "ComfyUI-VIBE",
"reference": "https://github.com/ato-zen/ComfyUI-VIBE",
"files": [
"https://github.com/ato-zen/ComfyUI-VIBE"
],
"install_type": "git-clone",
"description": "Implementation of VIBE (Visual Instruction Based Editor) as a custom node for ComfyUI that enables image editing using natural language instructions leveraging Sana1.5 and Qwen3-VL models."
},
{
"author": "FallenIncursio",
"title": "arcenciel-link-comfyui",
"reference": "https://github.com/FallenIncursio/arcenciel-link-comfyui",
"files": [
"https://github.com/FallenIncursio/arcenciel-link-comfyui"
],
"install_type": "git-clone",
"description": "Bring your ArcEnCiel models straight into ComfyUI with one click. Includes Link Key support, remote worker control, inventory sync, and sidecar generation."
},
{
"author": "0dot77",
"title": "comfyui-annotations",
"reference": "https://github.com/0dot77/comfyui-annotations",
"files": [
"https://github.com/0dot77/comfyui-annotations"
],
"install_type": "git-clone",
"description": "A lightweight annotation overlay for ComfyUI."
},
{
"author": "isala404",
"title": "comfy-workflow-api",
"reference": "https://github.com/isala404/comfy-workflow-api",
"files": [
"https://github.com/isala404/comfy-workflow-api"
],
"install_type": "git-clone",
"description": "HTTP API for ComfyUI with webhook-based workflow execution."
},

View File

@ -18,6 +18,17 @@
"title_aux": "mmaker/Color Enhance"
}
],
"https://github.com/0nikod/ComfyUI-Metadata-Tools": [
[
"ImageGetMetadata",
"ImageSetMetadata",
"LoadImageWithMetadata",
"SaveImageWithMetadata"
],
{
"title_aux": "ComfyUI-Metadata-Tools"
}
],
"https://github.com/0nikod/ComfyUI-Simple-Prompt": [
[
"SimplePrompt"
@ -2149,6 +2160,7 @@
"INPAINT_MaskedBlur",
"INPAINT_MaskedFill",
"INPAINT_ShrinkMask",
"INPAINT_StabilizeMask",
"INPAINT_VAEEncodeInpaintConditioning"
],
{
@ -6007,6 +6019,7 @@
"DiffusionModelSelectorNode",
"LORASelectorNode",
"ModelGeneratorNode",
"PonyPrefixesNode",
"SamplerGeneratorNode",
"SchedulerGeneratorNode",
"StringToFloatNode",
@ -22579,6 +22592,7 @@
"Qwen3VLAdvanced",
"Qwen3VLBasic",
"Qwen3VLExtraOptions",
"ResourceCleaner",
"Sa2VAAdvanced",
"Sa2VASegmentationPreset",
"ShowText",
@ -25024,6 +25038,14 @@
"title_aux": "ComfyUI NPNet (Golden Noise)"
}
],
"https://github.com/asagi4/comfyui-dynamic-anynode": [
[
"AnyNode"
],
{
"title_aux": "comfyui-dynamic-anynode"
}
],
"https://github.com/asagi4/comfyui-prompt-control": [
[
"PCAddMaskToCLIP",
@ -25143,6 +25165,14 @@
"title_aux": "comfyui_arcane_style_trans"
}
],
"https://github.com/ato-zen/ComfyUI-VIBE": [
[
"VIBE_Editor"
],
{
"title_aux": "ComfyUI-VIBE"
}
],
"https://github.com/ato321/ComfyUI-LTXVGuideRebase": [
[
"LTXVRebaseGuides"
@ -29792,11 +29822,13 @@
"ExtendIntermediateSigmas",
"FeatherMask",
"FlipSigmas",
"Flux2ProImageNode",
"Flux2Scheduler",
"FluxDisableGuidance",
"FluxGuidance",
"FluxKontextImageScale",
"FluxKontextMultiReferenceLatentMethod",
"FluxKontextProImageNode",
"FluxProExpandNode",
"FluxProFillNode",
"FluxProUltraImageNode",
@ -29840,6 +29872,7 @@
"ImageOnlyCheckpointLoader",
"ImageOnlyCheckpointSave",
"ImagePadForOutpaint",
"ImageProcessingNode",
"ImageQuantize",
"ImageRGBToYUV",
"ImageRotate",
@ -30201,6 +30234,7 @@
"TextEncodeHunyuanVideo_ImageToVideo",
"TextEncodeQwenImageEdit",
"TextEncodeQwenImageEditPlus",
"TextProcessingNode",
"ThresholdMask",
"TomePatchModel",
"TopazImageEnhance",
@ -32779,6 +32813,15 @@
"title_aux": "comfyui-videoframenode"
}
],
"https://github.com/ethanfel/ComfyUI-Sharp-Selector": [
[
"SharpFrameSelector",
"SharpnessAnalyzer"
],
{
"title_aux": "ComfyUI-Sharp-Selector"
}
],
"https://github.com/ethanfel/Comfyui-JSON-Manager": [
[
"JSONLoaderBatchI2V",
@ -35414,6 +35457,8 @@
"AIIA_CosyVoice_VoiceConversion",
"AIIA_Dialogue_TTS",
"AIIA_E2E_Speaker_Diarization",
"AIIA_EchoMimicLoader",
"AIIA_EchoMimicSampler",
"AIIA_FloatProcess_InMemory",
"AIIA_FloatProcess_ToDisk",
"AIIA_GenerateSpeakerSegments",
@ -37110,6 +37155,16 @@
"title_aux": "ComfyUI-DSD"
}
],
"https://github.com/isala404/comfy-workflow-api": [
[
"WebhookReceiver",
"WebhookSend",
"WebhookTransformer"
],
{
"title_aux": "comfy-workflow-api"
}
],
"https://github.com/iwanders/ComfyUI_nodes": [
[
"IW_JsonPickItem",
@ -40706,14 +40761,27 @@
],
"https://github.com/latentastronaut/comfyui-latent-astronaut-suite": [
[
"BatchLastImage",
"ChatterBoxTTS",
"ChatterBoxTTSLoader",
"ChatterBoxTTSLoaderAuto",
"ChatterBoxTTSSimple",
"ChatterBoxVC",
"ChatterBoxVCLoader",
"ChatterBoxVCLoaderAuto",
"ChatterBoxVCSimple",
"ForLoopEnd",
"ForLoopStart",
"ImageResizeToTotalPixels",
"LLMConfig",
"LLMPromptEnhancer",
"LoraLoaderModelOnlySelector",
"LoraLoaderSelector",
"SizeSelector",
"StringListCombine",
"StringListFromText",
"StringListIndex"
"StringListIndex",
"VideoLengthFromBatch"
],
{
"title_aux": "comfyui-latent-astronaut-suite"
@ -43540,6 +43608,18 @@
"title_aux": "comfyui-previewlatent"
}
],
"https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes": [
[
"EmptyZImageLatentImage",
"IllustrationStylePromptEncoder",
"PhotoStylePromptEncoder",
"SaveImage",
"ZSamplerTurbo"
],
{
"title_aux": "ComfyUI-ZImagePowerNodes"
}
],
"https://github.com/massao000/ComfyUI_aspect_ratios": [
[
"Aspect Ratios Node"
@ -53304,8 +53384,8 @@
],
"https://github.com/transcendedhacker/Mode_personal_node": [
[
"NegativeNode",
"PromptNode"
"NegativePromptNode",
"PromptComposerNode"
],
{
"title_aux": "Mode_personal_node"
@ -53629,7 +53709,9 @@
"LatentSmokeSimulation",
"LatentSwirlNoise",
"LatentWorleyNoise",
"SplitLatentPhaseMagnitude"
"PatchifyFlux2Latent",
"SplitLatentPhaseMagnitude",
"UnpatchifyFlux2Latent"
],
{
"title_aux": "Skoogeer-Noise"
@ -57251,6 +57333,7 @@
],
"https://github.com/zhangp365/ComfyUI-utils-nodes": [
[
"AspectRatioSizeNodeOfUtils",
"BooleanControlOutput",
"CheckpointLoaderSimpleWithSwitch",
"ColorCorrectOfUtils",

File diff suppressed because it is too large Load Diff

View File

@ -921,7 +921,7 @@ class UnifiedManager:
except:
return version.parse("0.0.0")
def execute_install_script(self, url, repo_path, instant_execution=False, lazy_mode=False, no_deps=False):
def execute_install_script(self, url, repo_path, instant_execution=False, lazy_mode=False, no_deps=False, selected_dependencies=None):
install_script_path = os.path.join(repo_path, "install.py")
requirements_path = os.path.join(repo_path, "requirements.txt")
@ -933,8 +933,19 @@ class UnifiedManager:
if os.path.exists(requirements_path) and not no_deps:
print("Install: pip packages")
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
# Create a set of selected dependency lines for quick lookup
selected_lines = set()
if selected_dependencies:
for dep in selected_dependencies:
selected_lines.add(dep.get('line', '').strip())
lines = manager_util.robust_readlines(requirements_path)
for line in lines:
# Skip if selected_dependencies is provided and this line is not in the selected list
if selected_dependencies is not None and line.strip() not in selected_lines:
continue
package_name = remap_pip_package(line.strip())
if package_name and not package_name.startswith('#') and package_name not in self.processed_install:
self.processed_install.add(package_name)
@ -1342,7 +1353,7 @@ class UnifiedManager:
return result
def repo_install(self, url: str, repo_path: str, instant_execution=False, no_deps=False, return_postinstall=False):
def repo_install(self, url: str, repo_path: str, instant_execution=False, no_deps=False, return_postinstall=False, selected_dependencies=None):
result = ManagedResult('install-git')
result.append(url)
@ -1369,7 +1380,7 @@ class UnifiedManager:
repo.close()
def postinstall():
return self.execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps)
return self.execute_install_script(url, repo_path, instant_execution=instant_execution, no_deps=no_deps, selected_dependencies=selected_dependencies)
if return_postinstall:
return result.with_postinstall(postinstall)
@ -1468,7 +1479,7 @@ class UnifiedManager:
else:
return self.cnr_switch_version(node_id, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall).with_ver('cnr')
async def install_by_id(self, node_id: str, version_spec=None, channel=None, mode=None, instant_execution=False, no_deps=False, return_postinstall=False):
async def install_by_id(self, node_id: str, version_spec=None, channel=None, mode=None, instant_execution=False, no_deps=False, return_postinstall=False, selected_dependencies=None):
"""
priority if version_spec == None
1. CNR latest
@ -1519,7 +1530,7 @@ class UnifiedManager:
self.unified_disable(node_id, False)
to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), node_id))
res = self.repo_install(repo_url, to_path, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall)
res = self.repo_install(repo_url, to_path, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall, selected_dependencies=selected_dependencies)
if res.result:
if version_spec == 'unknown':
self.unknown_active_nodes[node_id] = repo_url, to_path
@ -1968,7 +1979,7 @@ def __win_check_git_pull(path):
process.wait()
def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=False, no_deps=False):
def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=False, no_deps=False, selected_dependencies=None):
# import ipdb; ipdb.set_trace()
install_script_path = os.path.join(repo_path, "install.py")
requirements_path = os.path.join(repo_path, "requirements.txt")
@ -1980,6 +1991,13 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
if os.path.exists(requirements_path) and not no_deps:
print("Install: pip packages")
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
# Create a set of selected dependency lines for quick lookup
selected_lines = set()
if selected_dependencies:
for dep in selected_dependencies:
selected_lines.add(dep.get('line', '').strip())
with open(requirements_path, "r") as requirements_file:
for line in requirements_file:
#handle comments
@ -1990,6 +2008,10 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
else:
line = line.split('#')[0].strip()
# Skip if selected_dependencies is provided and this line is not in the selected list
if selected_dependencies is not None and line.strip() not in selected_lines:
continue
package_name = remap_pip_package(line.strip())
if package_name and not package_name.startswith('#'):

View File

@ -443,7 +443,12 @@ async def task_worker():
global tasks_in_progress
async def do_install(item) -> str:
ui_id, node_spec_str, channel, mode, skip_post_install = item
if len(item) == 6:
ui_id, node_spec_str, channel, mode, skip_post_install, selected_dependencies = item
else:
# Backward compatibility
ui_id, node_spec_str, channel, mode, skip_post_install = item
selected_dependencies = []
try:
node_spec = core.unified_manager.resolve_node_spec(node_spec_str)
@ -452,7 +457,7 @@ async def task_worker():
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)
res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install, selected_dependencies=selected_dependencies)
# discard post install if skip_post_install mode
if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
@ -1303,7 +1308,9 @@ 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")
install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install
# Get selected dependencies if provided
selected_dependencies = json_data.get('selectedDependencies', [])
install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install, selected_dependencies
task_queue.put(("install", install_item))
return web.Response(status=200)
@ -1383,6 +1390,272 @@ async def install_custom_node_pip(request):
return web.Response(status=200)
@routes.post("/customnode/analyze_dependencies")
async def analyze_dependencies(request):
"""
Analyze dependencies for a custom node from git URL.
Fetches requirements.txt, checks installed packages, and returns dependency list with status.
"""
try:
json_data = await request.json()
url = json_data.get('url')
commit_id = json_data.get('commitId')
branch = json_data.get('branch')
if not url:
return web.json_response({'error': 'URL is required'}, status=400)
# Fetch requirements.txt from git repository
requirements_content = await fetch_requirements_from_git(url, commit_id, branch)
if requirements_content is None:
return web.json_response({
'success': True,
'requirements': None,
'dependencies': [],
'noRequirementsFile': True
})
# Parse requirements
dependencies = parse_requirements(requirements_content)
# Get installed packages
installed_packages = manager_util.get_installed_packages()
# Analyze each dependency with subdependencies
analyzed_dependencies = []
for dep_line in dependencies:
if not dep_line.strip() or dep_line.strip().startswith('#'):
continue
# Parse dependency line
parsed = manager_util.parse_requirement_line(dep_line)
if not parsed:
continue
package_name = parsed.get('package')
if not package_name:
# Fallback: extract from line if package is missing
import re
match = re.match(r'^([a-zA-Z0-9_.-]+)', dep_line.strip())
package_name = match.group(1) if match else "Unknown"
version_spec = parsed.get('version')
# Convert version_spec to string if it's a StrictVersion object
if version_spec is not None:
version_spec = str(version_spec)
normalized_name = package_name.lower().replace('-', '_')
# Check if already installed
installed_version = installed_packages.get(normalized_name)
status = 'new'
if installed_version:
status = 'installed'
# Convert version to string if it's not already (handle StrictVersion objects)
current_version_str = str(installed_version) if installed_version else None
# Get subdependencies using pip install --dry-run
# This is optional and failures should not block the main flow
subdependencies = []
# Skip subdependency analysis for already installed packages (not needed)
if status != 'installed':
try:
import subprocess
import sys
# Run pip install --dry-run to get subdependencies
# Some packages like pymeshlab can take longer due to complex dependency resolution
# Use a reasonable timeout - if it times out, we'll continue without subdependencies
result = subprocess.run(
[sys.executable, '-m', 'pip', 'install', '--dry-run', dep_line.strip()],
capture_output=True,
text=True,
timeout=45 # Increased timeout to 45 seconds
)
output = result.stdout + result.stderr
if output:
subdependencies = parse_dry_run_output(output, package_name, installed_packages)
except subprocess.TimeoutExpired:
# Timeout is not critical - continue without subdependencies
logging.debug(f"Subdependency analysis timed out for {package_name} (skipping subdependencies)")
subdependencies = []
except Exception as e:
# Any other error is not critical - continue without subdependencies
logging.debug(f"Failed to analyze subdependencies for {package_name}: {e}")
subdependencies = []
# Add main dependency (always add, even if subdependency analysis failed)
# Ensure all fields are properly set and clean
clean_package_name = str(package_name).strip() if package_name else "Unknown"
# Remove any None/null strings that might have been concatenated
clean_package_name = clean_package_name.replace('None', '').replace('null', '').strip()
if not clean_package_name:
clean_package_name = "Unknown"
analyzed_dependencies.append({
'name': clean_package_name,
'version': str(version_spec) if version_spec else None,
'line': dep_line.strip(),
'status': status,
'currentVersion': current_version_str,
'selected': status != 'installed', # Deselect if already installed
'subdependencies': subdependencies
})
return web.json_response({
'success': True,
'requirements': requirements_content,
'dependencies': analyzed_dependencies,
'noRequirementsFile': False
})
except Exception as e:
logging.error(f"Error analyzing dependencies: {e}")
traceback.print_exc()
return web.json_response({'error': str(e)}, status=500)
def parse_requirements(content):
"""Parse requirements.txt content into list of dependency lines."""
lines = []
for line in content.split('\n'):
line = line.strip()
if line and not line.startswith('#'):
lines.append(line)
return lines
def parse_dry_run_output(output, parent_name, installed_packages):
"""Parse pip install --dry-run output to extract subdependencies."""
import re
subdependencies = []
subdeps_map = {}
lines = output.split('\n')
for line in lines:
line = line.strip()
# Look for "Collecting package==version" lines
if 'Collecting ' in line and 'Using cached' not in line:
# Match: "Collecting package==version" or "Collecting package"
match = re.search(r'Collecting\s+([a-zA-Z0-9_.-]+(?:\[[^\]]+\])?)(?:\s*==\s*([^\s\(]+))?', line)
if match:
dep_name = match.group(1).split('[')[0].strip()
# Clean the name - remove any None/null strings
if dep_name:
dep_name = dep_name.replace('None', '').replace('null', '').strip()
dep_version = match.group(2).strip() if match.group(2) else None
# Clean version too
if dep_version:
dep_version = dep_version.replace('None', '').replace('null', '').strip() or None
# Skip the parent package itself
if dep_name.lower() == parent_name.lower():
continue
# Normalize name
normalized_name = dep_name.lower().replace('-', '_')
# Check if already in map (avoid duplicates)
if normalized_name not in subdeps_map:
# Check if already installed
installed_version = installed_packages.get(normalized_name)
status = 'installed' if installed_version else 'new'
current_version_str = str(installed_version) if installed_version else None
# Ensure name is always a string, not None
if not dep_name:
dep_name = "Unknown"
# Clean the name - remove any None/null strings
clean_dep_name = str(dep_name).strip().replace('None', '').replace('null', '').strip()
if not clean_dep_name:
clean_dep_name = "Unknown"
subdeps_map[normalized_name] = {
'name': clean_dep_name,
'version': str(dep_version) if dep_version else None,
'status': status,
'currentVersion': current_version_str,
'selected': status != 'installed'
}
# Also look for "Would install" lines which have more accurate version info
if 'Would install' in line:
# Match: "Would install package-version"
match = re.search(r'Would install\s+([a-zA-Z0-9_.-]+)-([\d.]+)', line)
if match:
dep_name = match.group(1)
dep_version = match.group(2)
normalized_name = dep_name.lower().replace('-', '_')
if normalized_name in subdeps_map:
# Update with more accurate version
subdeps_map[normalized_name]['version'] = dep_version
# Convert map to list
for normalized_name, dep_info in subdeps_map.items():
subdependencies.append(dep_info)
return subdependencies
async def fetch_requirements_from_git(url, commit_id=None, branch=None):
"""
Fetch requirements.txt from a git repository URL.
Supports GitHub URLs by converting to raw.githubusercontent.com.
"""
try:
# Extract GitHub repo info
if 'github.com' in url:
# Convert to raw GitHub URL
url = url.rstrip('/')
if url.endswith('.git'):
url = url[:-4]
# Extract owner/repo
match = re.search(r'github\.com[:/]([^/]+)/([^/]+)', url)
if not match:
return None
owner = match.group(1)
repo = match.group(2)
# Build raw URL
if commit_id:
raw_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{commit_id}/requirements.txt"
elif branch:
raw_url = f"https://raw.githubusercontent.com/{owner}/{repo}/{branch}/requirements.txt"
else:
raw_url = f"https://raw.githubusercontent.com/{owner}/{repo}/main/requirements.txt"
# Try to fetch using aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(raw_url) as response:
if response.status == 200:
return await response.text()
# Try with master branch if main fails
if 'main' in raw_url:
raw_url = raw_url.replace('/main/', '/master/')
async with session.get(raw_url) as response2:
if response2.status == 200:
return await response2.text()
else:
# For non-GitHub URLs, we'd need to clone temporarily
# For now, return None (can be enhanced later)
logging.warning(f"Non-GitHub URL not fully supported for dependency analysis: {url}")
return None
return None
except Exception as e:
logging.error(f"Error fetching requirements from git: {e}")
return None
@routes.post("/manager/queue/uninstall")
async def uninstall_custom_node(request):
if not is_allowed_security_level('middle'):

View File

@ -67,6 +67,7 @@ export class CustomNodesManager {
this.filter = '';
this.keywords = '';
this.restartMap = {};
this.analyzeDependenciesBeforeInstall = false; // Default: false
this.init();
@ -77,6 +78,36 @@ export class CustomNodesManager {
}
init() {
// Create checkbox for dependency analysis
const analyzeDepsCheckbox = $el("input", {
type: "checkbox",
id: "cn-analyze-deps-checkbox",
checked: this.analyzeDependenciesBeforeInstall,
onchange: (e) => {
this.analyzeDependenciesBeforeInstall = e.target.checked;
},
style: {
marginRight: "6px",
cursor: "pointer"
}
});
const analyzeDepsLabel = $el("label", {
for: "cn-analyze-deps-checkbox",
style: {
display: "flex",
alignItems: "center",
cursor: "pointer",
color: "#fff",
fontSize: "12px",
marginRight: "10px",
whiteSpace: "nowrap"
}
}, [
analyzeDepsCheckbox,
$el("span", { textContent: "Analyse dependencies before node installation" })
]);
const header = $el("div.cn-manager-header.px-2", {}, [
// $el("label", {}, [
// $el("span", { textContent: "Filter" }),
@ -84,6 +115,7 @@ export class CustomNodesManager {
// ]),
createSettingsCombo("Filter", $el("select.cn-manager-filter")),
$el("input.cn-manager-keywords.p-inputtext.p-component", { type: "search", placeholder: "Search" }),
analyzeDepsLabel,
$el("div.cn-manager-status"),
$el("div.cn-flex-auto"),
$el("div.cn-manager-channel")
@ -105,6 +137,421 @@ export class CustomNodesManager {
this.initGrid();
}
showDependencySelectorDialog(dependencies, onSelect) {
const dialog = new ComfyDialog();
dialog.element.style.zIndex = 1100;
dialog.element.style.width = "900px";
dialog.element.style.maxHeight = "80vh";
dialog.element.style.padding = "0";
dialog.element.style.backgroundColor = "#2a2a2a";
dialog.element.style.border = "1px solid #3a3a3a";
dialog.element.style.borderRadius = "8px";
dialog.element.style.boxSizing = "border-box";
dialog.element.style.overflow = "hidden";
const contentStyle = {
width: "100%",
display: "flex",
flexDirection: "column",
padding: "20px",
boxSizing: "border-box",
gap: "15px"
};
// Create scrollable table container with sticky header
const tableContainer = $el("div", {
style: {
maxHeight: "500px",
overflowY: "auto",
border: "1px solid #4a4a4a",
borderRadius: "4px",
backgroundColor: "#1a1a1a",
position: "relative"
}
});
// Create table
const table = $el("table", {
style: {
width: "100%",
borderCollapse: "separate",
borderSpacing: "0",
fontSize: "14px"
}
});
// Create table header with sticky positioning
const thead = $el("thead", {
style: {
position: "sticky",
top: "0",
zIndex: "10",
backgroundColor: "#2a2a2a",
boxShadow: "0 2px 4px rgba(0,0,0,0.3)"
}
}, [
$el("tr", {
style: {
backgroundColor: "#2a2a2a",
borderBottom: "2px solid #4a4a4a"
}
}, [
$el("th", {
textContent: "",
style: {
padding: "10px",
textAlign: "left",
width: "40px",
color: "#fff"
}
}),
$el("th", {
textContent: "Dependency Name",
style: {
padding: "10px",
textAlign: "left",
color: "#fff",
fontWeight: "bold"
}
}),
$el("th", {
textContent: "Current Version",
style: {
padding: "10px",
textAlign: "left",
color: "#fff",
fontWeight: "bold"
}
}),
$el("th", {
textContent: "Incoming Version",
style: {
padding: "10px",
textAlign: "left",
color: "#fff",
fontWeight: "bold"
}
})
])
]);
// Create table body
const tbody = $el("tbody", {});
// Create table rows for each dependency and its subdependencies
let rowIndex = 0;
dependencies.forEach((dep) => {
// Ensure name is not null/undefined and clean it
let depName = dep.name;
if (!depName || depName === 'null' || depName === 'None') {
// Fallback: extract from line
if (dep.line) {
depName = dep.line.split(/[=<>!~]/)[0].trim();
} else {
depName = "Unknown";
}
}
// Remove any "null" suffix that might have been appended
depName = String(depName).replace(/null$/i, '').trim();
const isInstalled = dep.status === 'installed';
const incomingVersion = dep.version || "NA";
const currentVersion = dep.currentVersion || "NA";
// Main dependency row
const row = $el("tr", {
style: {
backgroundColor: rowIndex % 2 === 0 ? "#1a1a1a" : "#222222",
borderBottom: "1px solid #3a3a3a"
}
}, [
$el("td", {
style: {
padding: "10px",
textAlign: "center"
}
}, [
$el("input", {
type: "checkbox",
checked: dep.selected,
onchange: (e) => {
dep.selected = e.target.checked;
},
style: {
cursor: "pointer",
width: "18px",
height: "18px"
}
})
]),
$el("td", {
style: {
padding: "10px",
color: isInstalled ? "#888" : "#fff"
}
}, [
$el("span", {
textContent: depName,
style: {
fontWeight: "500",
marginRight: isInstalled ? "8px" : "0"
}
}),
isInstalled ? $el("span", {
textContent: "Installed",
style: {
display: "inline-block",
backgroundColor: "#2a4a2a",
color: "#4a9",
padding: "2px 6px",
borderRadius: "3px",
fontSize: "10px",
fontWeight: "bold",
border: "1px solid #4a9"
}
}) : ''
]),
$el("td", {
textContent: currentVersion,
style: {
padding: "10px",
color: isInstalled ? "#4a9" : "#aaa",
fontFamily: "monospace"
}
}),
$el("td", {
textContent: incomingVersion,
style: {
padding: "10px",
color: "#fff",
fontFamily: "monospace"
}
})
]);
tbody.appendChild(row);
rowIndex++;
// Add subdependencies as indented rows
if(dep.subdependencies && dep.subdependencies.length > 0) {
dep.subdependencies.forEach((subdep) => {
// Ensure subdependency name is not null/undefined and clean it
let subdepName = subdep.name;
if (!subdepName || subdepName === 'null' || subdepName === 'None') {
subdepName = "Unknown";
}
// Remove any "null" suffix that might have been appended
subdepName = String(subdepName).replace(/null$/i, '').trim();
const subIsInstalled = subdep.status === 'installed';
const subIncomingVersion = subdep.version || "NA";
const subCurrentVersion = subdep.currentVersion || "NA";
const subRow = $el("tr", {
style: {
backgroundColor: rowIndex % 2 === 0 ? "#1a1a1a" : "#222222",
borderBottom: "1px solid #3a3a3a"
}
}, [
$el("td", {
style: {
padding: "10px",
textAlign: "center"
}
}, [
$el("input", {
type: "checkbox",
checked: subdep.selected,
onchange: (e) => {
subdep.selected = e.target.checked;
},
style: {
cursor: "pointer",
width: "18px",
height: "18px"
}
})
]),
$el("td", {
style: {
padding: "10px 10px 10px 30px",
color: subIsInstalled ? "#888" : "#aaa",
fontSize: "13px"
}
}, [
$el("span", {
textContent: "└─ " + subdepName,
style: {
fontWeight: "400",
marginRight: subIsInstalled ? "8px" : "0"
}
}),
subIsInstalled ? $el("span", {
textContent: "Installed",
style: {
display: "inline-block",
backgroundColor: "#2a4a2a",
color: "#4a9",
padding: "2px 6px",
borderRadius: "3px",
fontSize: "10px",
fontWeight: "bold",
border: "1px solid #4a9"
}
}) : ''
]),
$el("td", {
textContent: subCurrentVersion,
style: {
padding: "10px",
color: subIsInstalled ? "#4a9" : "#666",
fontFamily: "monospace",
fontSize: "13px"
}
}),
$el("td", {
textContent: subIncomingVersion,
style: {
padding: "10px",
color: "#aaa",
fontFamily: "monospace",
fontSize: "13px"
}
})
]);
tbody.appendChild(subRow);
rowIndex++;
});
}
});
table.appendChild(thead);
table.appendChild(tbody);
tableContainer.appendChild(table);
const content = $el("div", {
style: contentStyle
}, [
$el("h3", {
textContent: "Select Dependencies to Install",
style: {
color: "#ffffff",
backgroundColor: "#1a1a1a",
padding: "10px 15px",
margin: "0 0 10px 0",
width: "100%",
textAlign: "center",
borderRadius: "4px",
boxSizing: "border-box"
}
}),
$el("div", {
textContent: `${dependencies.filter(d => d.status === 'installed').length} already installed, ${dependencies.filter(d => d.status !== 'installed').length} to install`,
style: {
color: "#aaa",
fontSize: "12px",
marginBottom: "5px"
}
}),
tableContainer,
$el("div", {
style: {
display: "flex",
justifyContent: "space-between",
width: "100%",
gap: "10px",
marginTop: "10px"
}
}, [
$el("button", {
textContent: "Cancel",
onclick: () => {
onSelect(null); // Pass null to indicate cancellation
dialog.close();
},
style: {
flex: "1",
padding: "8px",
backgroundColor: "#4a4a4a",
color: "#ffffff",
border: "none",
borderRadius: "4px",
cursor: "pointer"
}
}),
$el("button", {
textContent: "Select All",
onclick: () => {
dependencies.forEach(dep => {
if (dep.status !== 'installed') {
dep.selected = true;
}
// Also select subdependencies
if(dep.subdependencies) {
dep.subdependencies.forEach(subdep => {
if(subdep.status !== 'installed') {
subdep.selected = true;
}
});
}
});
// Update checkboxes in the table
const checkboxes = tableContainer.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach((checkbox) => {
if(!checkbox.disabled) {
checkbox.checked = true;
}
});
},
style: {
padding: "8px 15px",
backgroundColor: "#4a6a4a",
color: "#ffffff",
border: "none",
borderRadius: "4px",
cursor: "pointer"
}
}),
$el("button", {
textContent: "Install Selected",
onclick: () => {
// Collect all selected dependencies (main + subdependencies)
const selected = [];
dependencies.forEach(d => {
if(d.selected) {
selected.push(d);
}
// Also include selected subdependencies
if(d.subdependencies) {
d.subdependencies.forEach(subdep => {
if(subdep.selected) {
selected.push(subdep);
}
});
}
});
onSelect(selected);
dialog.close();
},
style: {
flex: "1",
padding: "8px",
backgroundColor: "#4CAF50",
color: "#ffffff",
border: "none",
borderRadius: "4px",
cursor: "pointer"
}
}),
])
]);
console.log('[Dependency Dialog] Showing dialog with', dependencies.length, 'dependencies');
dialog.show(content);
console.log('[Dependency Dialog] Dialog shown');
}
showVersionSelectorDialog(versions, onSelect) {
const dialog = new ComfyDialog();
dialog.element.style.zIndex = 1100;
@ -1470,6 +1917,101 @@ export class CustomNodesManager {
}
}
// For install mode, analyze dependencies BEFORE starting installation
let selectedDependencies = [];
let dependencyDialogShown = false; // Track if dialog was shown
if(mode === "install" && this.analyzeDependenciesBeforeInstall) {
// Analyze dependencies for all items first (only if checkbox is enabled)
for (const hash of list) {
const item = this.grid.getRowItemBy("hash", hash);
if (!item) {
console.log('[Dependency Analysis] Item not found for hash:', hash);
continue;
}
const data = item.originalData;
console.log('[Dependency Analysis] Item data:', {
title: item.title,
files: data.files,
repository: data.repository,
hasFiles: !!data.files,
filesLength: data.files ? data.files.length : 0
});
// Try multiple ways to get the git URL
let gitUrl = null;
if(data.files && data.files.length > 0) {
gitUrl = data.files[0];
} else if(data.repository) {
gitUrl = data.repository;
}
if(gitUrl && (gitUrl.includes('github.com') || gitUrl.includes('.git'))) {
try {
this.showStatus(`Analyzing dependencies for ${item.title}...`);
console.log('[Dependency Analysis] Fetching dependencies for:', gitUrl);
const analyzeRes = await api.fetchApi('/customnode/analyze_dependencies', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: gitUrl,
commitId: data.commit_id,
branch: data.branch
})
});
console.log('[Dependency Analysis] Response status:', analyzeRes.status);
if(analyzeRes.status === 200) {
const analyzeData = await analyzeRes.json();
console.log('[Dependency Analysis] Response data:', {
success: analyzeData.success,
hasDependencies: !!analyzeData.dependencies,
dependenciesCount: analyzeData.dependencies ? analyzeData.dependencies.length : 0,
noRequirementsFile: analyzeData.noRequirementsFile
});
if(analyzeData.success && analyzeData.dependencies && analyzeData.dependencies.length > 0) {
console.log('[Dependency Analysis] Showing dialog with', analyzeData.dependencies.length, 'dependencies');
dependencyDialogShown = true;
// Show dependency selection dialog and wait for user
const userSelection = await new Promise((resolve) => {
this.showDependencySelectorDialog(analyzeData.dependencies, (selected) => {
console.log('[Dependency Analysis] User selected:', selected);
resolve(selected);
});
});
// If user cancelled (null), stop installation
if(userSelection === null) {
console.log('[Dependency Analysis] User cancelled installation');
this.showStatus("Installation cancelled");
return;
}
selectedDependencies = userSelection || [];
console.log('[Dependency Analysis] Selected dependencies:', selectedDependencies.length);
} else if(analyzeData.noRequirementsFile) {
console.log('[Dependency Analysis] No requirements.txt file found');
} else {
console.log('[Dependency Analysis] No dependencies to show');
}
} else {
const errorText = await analyzeRes.text();
console.error('[Dependency Analysis] API error:', analyzeRes.status, errorText);
}
} catch(e) {
console.error('[Dependency Analysis] Exception:', e);
// Continue with installation even if dependency analysis fails
}
} else {
console.log('[Dependency Analysis] Not a GitHub URL or no URL found:', gitUrl);
}
}
}
target.classList.add("cn-btn-loading");
this.showError("");
@ -1505,6 +2047,46 @@ export class CustomNodesManager {
data.mode = this.mode;
data.ui_id = hash;
// Add selected dependencies to data (including subdependencies)
// Only install selected dependencies - respect user's selection
const allSelected = [];
if(selectedDependencies.length > 0) {
selectedDependencies.forEach(d => {
// Add main dependency if selected
if(d.selected) {
allSelected.push({
name: d.name,
version: d.version,
line: d.line
});
}
// Add selected subdependencies
if(d.subdependencies) {
d.subdependencies.forEach(subdep => {
if(subdep.selected) {
allSelected.push({
name: subdep.name,
version: subdep.version,
line: `${subdep.name}${subdep.version ? '==' + subdep.version : ''}`
});
}
});
}
});
}
// Set selectedDependencies:
// - If dialog was shown: always set (even if empty) to respect user's selection
// - If dialog was not shown: don't set (install all dependencies - original behavior)
if(dependencyDialogShown) {
// User saw the dialog, respect their selection (even if empty)
data.selectedDependencies = allSelected;
} else if(allSelected.length > 0) {
// Dialog wasn't shown but we have selections (shouldn't happen, but just in case)
data.selectedDependencies = allSelected;
}
// If dialog wasn't shown and no selections, don't set selectedDependencies
// This means backend will install all dependencies (original behavior)
let install_mode = mode;
if(mode == 'switch') {
install_mode = 'install';

View File

@ -1,5 +1,85 @@
{
"custom_nodes": [
{
"author": "ah-kun",
"title": "ComfyUI-OTP-Auth",
"reference": "https://github.com/ah-kun/ComfyUI-OTP-Auth",
"files": [
"https://github.com/ah-kun/ComfyUI-OTP-Auth"
],
"install_type": "git-clone",
"description": "A custom node for ComfyUI that adds simple One-Time Password (OTP) authentication using Google Authenticator to prevent unauthorized use on publicly accessible servers."
},
{
"author": "pollockjj",
"title": "ComfyUI-StabilityTest",
"reference": "https://github.com/pollockjj/ComfyUI-StabilityTest",
"files": [
"https://github.com/pollockjj/ComfyUI-StabilityTest"
],
"install_type": "git-clone",
"description": "ComfyUI stability testing node. (Description by CC)"
},
{
"author": "Tr1dae",
"title": "[WIP] ComfyUI-MobileSAM",
"reference": "https://github.com/Tr1dae/ComfyUI-MobileSAM",
"files": [
"https://github.com/Tr1dae/ComfyUI-MobileSAM"
],
"install_type": "git-clone",
"description": "A ComfyUI custom node for text-guided image segmentation using GroundingDINO and MobileSAM to segment objects in images using natural language prompts.\nNOTE: The files in the repo are not organized."
},
{
"author": "Vov1ch",
"title": "ComfyUI_GLMImage",
"reference": "https://github.com/Vov1ch/ComfyUI_GLMImage",
"files": [
"https://github.com/Vov1ch/ComfyUI_GLMImage"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for GLM image generation, image-to-image translation, and flexible input handling. (Description by CC)"
},
{
"author": "DailyMok",
"title": "ComfyUI-PromptMixerNode",
"reference": "https://github.com/DailyMok/ComfyUI-PromptMixerNode",
"files": [
"https://github.com/DailyMok/ComfyUI-PromptMixerNode"
],
"install_type": "git-clone",
"description": "ComfyUI custom node for prompt mixing with PromptMixerDaily node. (Description by CC)"
},
{
"author": "shin131002",
"title": "[WIP] ComfyUI-Prompt-Preset-Selector",
"reference": "https://github.com/shin131002/ComfyUI-Prompt-Preset-Selector",
"files": [
"https://github.com/shin131002/ComfyUI-Prompt-Preset-Selector"
],
"install_type": "git-clone",
"description": "Flexible preset selector with YAML support, advanced keyword filtering, and hierarchical key search\nNOTE: The files in the repo are not organized."
},
{
"author": "fogyisland",
"title": "Comfy_Show_StringText [WIP]",
"reference": "https://github.com/fogyisland/Comfy_Show_StringText",
"files": [
"https://github.com/fogyisland/Comfy_Show_StringText"
],
"install_type": "git-clone",
"description": "ComfyUI node for text display that implements string data output. (Description by CC)\nNOTE: The files in the repo are not organized."
},
{
"author": "Leecoahs",
"title": "ComfyUI_LeeNodes",
"reference": "https://github.com/Leecoahs/ComfyUI_LeeNodes",
"files": [
"https://github.com/Leecoahs/ComfyUI_LeeNodes"
],
"install_type": "git-clone",
"description": "ComfyUI nodes for image processing including grayscale combining, overlaying, texture inpainting. (Description by CC)"
},
{
"author": "cosmicbuffalo",
"title": "comfyui-mobile-frontend",

View File

@ -274,6 +274,7 @@
"SimpleIntMathHandle",
"SimpleJsonArrayHandle",
"SimpleJsonObjectHandle",
"TestExtendComfyNode",
"VideoFrameSize",
"VideoSizeAndFps",
"VideoTimeAndFPS",
@ -1523,6 +1524,14 @@
"title_aux": "ComfyUI-VideoStream"
}
],
"https://github.com/DailyMok/ComfyUI-PromptMixerNode": [
[
"PromptMixerDaily"
],
{
"title_aux": "ComfyUI-PromptMixerNode"
}
],
"https://github.com/DataCTE/ComfyUI-DataVoid-nodes": [
[
"IPAAdapterFaceIDBatch",
@ -2817,6 +2826,17 @@
"title_aux": "ComfyUI-ModelUnloader"
}
],
"https://github.com/Leecoahs/ComfyUI_LeeNodes": [
[
"CombineGrayscaleMRH",
"ImageOverlaySimple",
"TextureInpaintingPrep",
"TextureInpaintingRestore"
],
{
"title_aux": "ComfyUI_LeeNodes"
}
],
"https://github.com/Letz-AI/ComfyUI-LetzAI": [
[
"LetzAI Generator"
@ -3728,6 +3748,14 @@
"title_aux": "DoomFLUX Nodes [WIP]"
}
],
"https://github.com/PladsElsker/comfyui-krita": [
[
"KritaSaveImage"
],
{
"title_aux": "comfyui-krita [WIP]"
}
],
"https://github.com/Polygoningenieur/ComfyUI-Diffusion-SDXL-Video": [
[
"DiffusionSDXLFrameByFrame"
@ -4986,17 +5014,29 @@
"https://github.com/ThatGlennD/ComfyUI-Image-Analysis-Tools": [
[
"Blur Detection",
"BlurDetection",
"Clipping Analysis",
"ClippingAnalysis",
"Color Cast Detector",
"Color Harmony Analyzer",
"Color Temperature Estimator",
"ColorCastDetector",
"ColorHarmonyAnalyzer",
"ColorTemperatureEstimator",
"Contrast Analysis",
"ContrastAnalysis",
"Defocus Analysis",
"DefocusAnalysis",
"Edge Density Analysis",
"EdgeDensityAnalysis",
"Entropy Analysis",
"EntropyAnalysis",
"Noise Estimation",
"NoiseEstimation",
"RGB Histogram Renderer",
"Sharpness / Focus Score"
"RGBHistogramRenderer",
"Sharpness / Focus Score",
"SharpnessFocusScore"
],
{
"title_aux": "ComfyUI Image Analysis Toolkit [WIP]"
@ -5106,6 +5146,14 @@
"title_aux": "ComfyUI-CustomNodes-MVM"
}
],
"https://github.com/Tr1dae/ComfyUI-MobileSAM": [
[
"EasyMobileSAM"
],
{
"title_aux": "[WIP] ComfyUI-MobileSAM"
}
],
"https://github.com/UltraNoob-NazoGiken/ComfyUI-TOML-Tools": [
[
"CreateTomlData",
@ -5203,6 +5251,23 @@
"title_aux": "ComfyUI Custom Nodes: OpenRouter & Ollama [UNSAFE]"
}
],
"https://github.com/Vov1ch/ComfyUI_GLMImage": [
[
"GLMImageGenerate",
"GLMImageImageToImage",
"GLMImageSDNQ_FlexibleInput",
"GLMImageSDNQ_Generate",
"GLMImageSDNQ_I2I_Standalone",
"GLMImageSDNQ_ImageToImage",
"GLMImageSDNQ_LoadPipe",
"GLMImageSDNQ_MultiI2I_Standalone",
"GLMImageSDNQ_MultiImageToImage",
"GLMImageSDNQ_T2I_Standalone"
],
{
"title_aux": "ComfyUI_GLMImage"
}
],
"https://github.com/Vsolon/ComfyUI-CBZ-Pack": [
[
"CBZ Preview Any",
@ -6763,6 +6828,7 @@
[
"AliyunDriveCloudUploadNode",
"AliyunDriveOptimizedUploadNode",
"AliyunDriveUploadNode",
"SimpleUploadToAliyunDrive",
"UploadTo115",
"UploadToAliyunDrive",
@ -6972,11 +7038,13 @@
"ExtendIntermediateSigmas",
"FeatherMask",
"FlipSigmas",
"Flux2ProImageNode",
"Flux2Scheduler",
"FluxDisableGuidance",
"FluxGuidance",
"FluxKontextImageScale",
"FluxKontextMultiReferenceLatentMethod",
"FluxKontextProImageNode",
"FluxProExpandNode",
"FluxProFillNode",
"FluxProUltraImageNode",
@ -7020,6 +7088,7 @@
"ImageOnlyCheckpointLoader",
"ImageOnlyCheckpointSave",
"ImagePadForOutpaint",
"ImageProcessingNode",
"ImageQuantize",
"ImageRGBToYUV",
"ImageRotate",
@ -7381,6 +7450,7 @@
"TextEncodeHunyuanVideo_ImageToVideo",
"TextEncodeQwenImageEdit",
"TextEncodeQwenImageEditPlus",
"TextProcessingNode",
"ThresholdMask",
"TomePatchModel",
"TopazImageEnhance",
@ -8245,6 +8315,14 @@
"title_aux": "comfyui-cem-tools"
}
],
"https://github.com/fogyisland/Comfy_Show_StringText": [
[
"ComfyUIShowText"
],
{
"title_aux": "Comfy_Show_StringText [WIP]"
}
],
"https://github.com/franklydegenerate/ComfyUI-WAN-Resolution-Helper": [
[
"WANResolutionHelperV2"
@ -8286,6 +8364,43 @@
"title_aux": "ComfyUI-LLM-Utils [WIP]"
}
],
"https://github.com/frost-byte/fbTools": [
[
"FBTextEncodeQwenImageEditPlus",
"LibberApply",
"LibberManager",
"MaskProcessor",
"NodeInputSelect",
"OpaqueAlpha",
"PromptComposer",
"QwenAspectRatio",
"SAMPreprocessNHWC",
"SceneCreate",
"SceneInput",
"SceneOutput",
"ScenePromptManager",
"SceneSave",
"SceneSelect",
"SceneUpdate",
"SceneView",
"SceneWanVideoLoraMultiSave",
"StoryCreate",
"StoryEdit",
"StoryLoad",
"StorySave",
"StorySceneBatch",
"StorySceneImageSave",
"StoryScenePick",
"StoryVideoBatch",
"StoryView",
"SubdirLister",
"TailEnhancePro",
"TailSplit"
],
{
"title_aux": "fb-tools"
}
],
"https://github.com/ftechmax/ComfyUI-NovaKit-Pack": [
[
"CountTokens"
@ -8548,6 +8663,7 @@
],
"https://github.com/grinlau18/ComfyUI_XISER_Nodes": [
[
"BaseFromListGetOneV3",
"XISER_Canvas",
"XIS_BooleanSwitch",
"XIS_CanvasConfig",
@ -12388,6 +12504,14 @@
"title_aux": "ComfyUI-JsonViewer [WIP]"
}
],
"https://github.com/shin131002/ComfyUI-Prompt-Preset-Selector": [
[
"PromptPresetSelector"
],
{
"title_aux": "[WIP] ComfyUI-Prompt-Preset-Selector"
}
],
"https://github.com/shinich39/comfyui-run-js": [
[
"RunJS"

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,76 @@
{
"custom_nodes": [
{
"author": "martin-rizzo",
"title": "ComfyUI-ZImagePowerNodes",
"reference": "https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes",
"files": [
"https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes"
],
"install_type": "git-clone",
"description": "A set of ComfyUI nodes designed and fine-tuned specifically for the Z-Image model. Pushing the best image generation model to its limits!"
},
{
"author": "0dot77",
"title": "comfyui-annotations",
"reference": "https://github.com/0dot77/comfyui-annotations",
"files": [
"https://github.com/0dot77/comfyui-annotations"
],
"install_type": "git-clone",
"description": "A lightweight annotation overlay for ComfyUI."
},
{
"author": "isala404",
"title": "comfy-workflow-api",
"reference": "https://github.com/isala404/comfy-workflow-api",
"files": [
"https://github.com/isala404/comfy-workflow-api"
],
"install_type": "git-clone",
"description": "HTTP API for ComfyUI with webhook-based workflow execution."
},
{
"author": "0nikod",
"title": "ComfyUI-Metadata-Tools",
"reference": "https://github.com/0nikod/ComfyUI-Metadata-Tools",
"files": [
"https://github.com/0nikod/ComfyUI-Metadata-Tools"
],
"install_type": "git-clone",
"description": "Tools about metadata."
},
{
"author": "ethanfel",
"title": "ComfyUI-Sharp-Selector",
"reference": "https://github.com/ethanfel/ComfyUI-Sharp-Selector",
"files": [
"https://github.com/ethanfel/ComfyUI-Sharp-Selector"
],
"install_type": "git-clone",
"description": "Custom nodes for sharp frame selection and analysis. (Description by CC)"
},
{
"author": "ato-zen",
"title": "ComfyUI-VIBE",
"reference": "https://github.com/ato-zen/ComfyUI-VIBE",
"files": [
"https://github.com/ato-zen/ComfyUI-VIBE"
],
"install_type": "git-clone",
"description": "Implementation of VIBE (Visual Instruction Based Editor) as a custom node for ComfyUI that enables image editing using natural language instructions leveraging Sana1.5 and Qwen3-VL models."
},
{
"author": "FallenIncursio",
"title": "arcenciel-link-comfyui",
"reference": "https://github.com/FallenIncursio/arcenciel-link-comfyui",
"files": [
"https://github.com/FallenIncursio/arcenciel-link-comfyui"
],
"install_type": "git-clone",
"description": "Bring your ArcEnCiel models straight into ComfyUI with one click. Includes Link Key support, remote worker control, inventory sync, and sidecar generation."
},
{
"author": "capitan01R",
"title": "ComfyUI-CapitanFlowMatch",
@ -635,57 +706,6 @@
],
"install_type": "git-clone",
"description": "A dedicated ComfyUI custom node designed to streamline the process of converting transparent images (RGBA) into masks and high-quality mask previews."
},
{
"author": "wallen0322",
"title": "ComfyUI-qwenmultianglelight",
"reference": "https://github.com/wallen0322/ComfyUI-qwenmultianglelight",
"files": [
"https://github.com/wallen0322/ComfyUI-qwenmultianglelight"
],
"install_type": "git-clone",
"description": "A ComfyUI node for 3D lighting angle control, outputs lighting prompts for relighting image generation"
},
{
"author": "patientx",
"title": "CFZ-SwitchMenu",
"reference": "https://github.com/patientx/CFZ-SwitchMenu",
"files": [
"https://github.com/patientx/CFZ-SwitchMenu"
],
"install_type": "git-clone",
"description": "Adds a menu button that switches between old and new style ComfyUI menus."
},
{
"author": "Lumiyumi",
"title": "LumyINTP",
"reference": "https://github.com/Lumiyumi/LumyINTP",
"files": [
"https://github.com/Lumiyumi/LumyINTP"
],
"install_type": "git-clone",
"description": "INT+ node for ComfyUI with control after generate style functionality for signal-controlled increment/decrement/randomize operations."
},
{
"author": "MajoorWaldi",
"title": "ComfyUI-Majoor-ImageOps",
"reference": "https://github.com/MajoorWaldi/ComfyUI-Majoor-ImageOps",
"files": [
"https://github.com/MajoorWaldi/ComfyUI-Majoor-ImageOps"
],
"install_type": "git-clone",
"description": "Essential Nodes Pack for Images Processing for ComfyUI, with a live embedded preview inside the node (no queue) for supported chains."
},
{
"author": "fujitea40",
"title": "comfy_exp_preset",
"reference": "https://github.com/fujitea40/comfy_exp_preset",
"files": [
"https://github.com/fujitea40/comfy_exp_preset"
],
"install_type": "git-clone",
"description": "Custom node for expression preset management in ComfyUI, allowing users to preselect expressions and parameters for character animation. (Description by CC)"
}
]
}

View File

@ -18,6 +18,17 @@
"title_aux": "mmaker/Color Enhance"
}
],
"https://github.com/0nikod/ComfyUI-Metadata-Tools": [
[
"ImageGetMetadata",
"ImageSetMetadata",
"LoadImageWithMetadata",
"SaveImageWithMetadata"
],
{
"title_aux": "ComfyUI-Metadata-Tools"
}
],
"https://github.com/0nikod/ComfyUI-Simple-Prompt": [
[
"SimplePrompt"
@ -2149,6 +2160,7 @@
"INPAINT_MaskedBlur",
"INPAINT_MaskedFill",
"INPAINT_ShrinkMask",
"INPAINT_StabilizeMask",
"INPAINT_VAEEncodeInpaintConditioning"
],
{
@ -6007,6 +6019,7 @@
"DiffusionModelSelectorNode",
"LORASelectorNode",
"ModelGeneratorNode",
"PonyPrefixesNode",
"SamplerGeneratorNode",
"SchedulerGeneratorNode",
"StringToFloatNode",
@ -22579,6 +22592,7 @@
"Qwen3VLAdvanced",
"Qwen3VLBasic",
"Qwen3VLExtraOptions",
"ResourceCleaner",
"Sa2VAAdvanced",
"Sa2VASegmentationPreset",
"ShowText",
@ -25024,6 +25038,14 @@
"title_aux": "ComfyUI NPNet (Golden Noise)"
}
],
"https://github.com/asagi4/comfyui-dynamic-anynode": [
[
"AnyNode"
],
{
"title_aux": "comfyui-dynamic-anynode"
}
],
"https://github.com/asagi4/comfyui-prompt-control": [
[
"PCAddMaskToCLIP",
@ -25143,6 +25165,14 @@
"title_aux": "comfyui_arcane_style_trans"
}
],
"https://github.com/ato-zen/ComfyUI-VIBE": [
[
"VIBE_Editor"
],
{
"title_aux": "ComfyUI-VIBE"
}
],
"https://github.com/ato321/ComfyUI-LTXVGuideRebase": [
[
"LTXVRebaseGuides"
@ -29792,11 +29822,13 @@
"ExtendIntermediateSigmas",
"FeatherMask",
"FlipSigmas",
"Flux2ProImageNode",
"Flux2Scheduler",
"FluxDisableGuidance",
"FluxGuidance",
"FluxKontextImageScale",
"FluxKontextMultiReferenceLatentMethod",
"FluxKontextProImageNode",
"FluxProExpandNode",
"FluxProFillNode",
"FluxProUltraImageNode",
@ -29840,6 +29872,7 @@
"ImageOnlyCheckpointLoader",
"ImageOnlyCheckpointSave",
"ImagePadForOutpaint",
"ImageProcessingNode",
"ImageQuantize",
"ImageRGBToYUV",
"ImageRotate",
@ -30201,6 +30234,7 @@
"TextEncodeHunyuanVideo_ImageToVideo",
"TextEncodeQwenImageEdit",
"TextEncodeQwenImageEditPlus",
"TextProcessingNode",
"ThresholdMask",
"TomePatchModel",
"TopazImageEnhance",
@ -32779,6 +32813,15 @@
"title_aux": "comfyui-videoframenode"
}
],
"https://github.com/ethanfel/ComfyUI-Sharp-Selector": [
[
"SharpFrameSelector",
"SharpnessAnalyzer"
],
{
"title_aux": "ComfyUI-Sharp-Selector"
}
],
"https://github.com/ethanfel/Comfyui-JSON-Manager": [
[
"JSONLoaderBatchI2V",
@ -35414,6 +35457,8 @@
"AIIA_CosyVoice_VoiceConversion",
"AIIA_Dialogue_TTS",
"AIIA_E2E_Speaker_Diarization",
"AIIA_EchoMimicLoader",
"AIIA_EchoMimicSampler",
"AIIA_FloatProcess_InMemory",
"AIIA_FloatProcess_ToDisk",
"AIIA_GenerateSpeakerSegments",
@ -37110,6 +37155,16 @@
"title_aux": "ComfyUI-DSD"
}
],
"https://github.com/isala404/comfy-workflow-api": [
[
"WebhookReceiver",
"WebhookSend",
"WebhookTransformer"
],
{
"title_aux": "comfy-workflow-api"
}
],
"https://github.com/iwanders/ComfyUI_nodes": [
[
"IW_JsonPickItem",
@ -40706,14 +40761,27 @@
],
"https://github.com/latentastronaut/comfyui-latent-astronaut-suite": [
[
"BatchLastImage",
"ChatterBoxTTS",
"ChatterBoxTTSLoader",
"ChatterBoxTTSLoaderAuto",
"ChatterBoxTTSSimple",
"ChatterBoxVC",
"ChatterBoxVCLoader",
"ChatterBoxVCLoaderAuto",
"ChatterBoxVCSimple",
"ForLoopEnd",
"ForLoopStart",
"ImageResizeToTotalPixels",
"LLMConfig",
"LLMPromptEnhancer",
"LoraLoaderModelOnlySelector",
"LoraLoaderSelector",
"SizeSelector",
"StringListCombine",
"StringListFromText",
"StringListIndex"
"StringListIndex",
"VideoLengthFromBatch"
],
{
"title_aux": "comfyui-latent-astronaut-suite"
@ -43540,6 +43608,18 @@
"title_aux": "comfyui-previewlatent"
}
],
"https://github.com/martin-rizzo/ComfyUI-ZImagePowerNodes": [
[
"EmptyZImageLatentImage",
"IllustrationStylePromptEncoder",
"PhotoStylePromptEncoder",
"SaveImage",
"ZSamplerTurbo"
],
{
"title_aux": "ComfyUI-ZImagePowerNodes"
}
],
"https://github.com/massao000/ComfyUI_aspect_ratios": [
[
"Aspect Ratios Node"
@ -53304,8 +53384,8 @@
],
"https://github.com/transcendedhacker/Mode_personal_node": [
[
"NegativeNode",
"PromptNode"
"NegativePromptNode",
"PromptComposerNode"
],
{
"title_aux": "Mode_personal_node"
@ -53629,7 +53709,9 @@
"LatentSmokeSimulation",
"LatentSwirlNoise",
"LatentWorleyNoise",
"SplitLatentPhaseMagnitude"
"PatchifyFlux2Latent",
"SplitLatentPhaseMagnitude",
"UnpatchifyFlux2Latent"
],
{
"title_aux": "Skoogeer-Noise"
@ -57251,6 +57333,7 @@
],
"https://github.com/zhangp365/ComfyUI-utils-nodes": [
[
"AspectRatioSizeNodeOfUtils",
"BooleanControlOutput",
"CheckpointLoaderSimpleWithSwitch",
"ColorCorrectOfUtils",