mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2026-07-11 17:07:08 +08:00
Compare commits
9 Commits
17c0923cfc
...
d916184985
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d916184985 | ||
|
|
e11e9034ce | ||
|
|
fb75c048bd | ||
|
|
f7fd701e19 | ||
|
|
cf883eb762 | ||
|
|
45891097bb | ||
|
|
2757728935 | ||
|
|
bb46126fbd | ||
|
|
af1c698117 |
@ -33152,6 +33152,9 @@
|
||||
"https://github.com/Azornes/Comfyui-Resolution-Master"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"preemptions": [
|
||||
"ResolutionMaster"
|
||||
],
|
||||
"description": " Precise resolution and aspect ratio control for ComfyUI"
|
||||
},
|
||||
{
|
||||
@ -54041,7 +54044,7 @@
|
||||
"https://github.com/kgilper/krea-reference"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Give each reference image a job before it reaches Krea 2. Guide cards assign per-image roles (keep the same subject, suggest the visual style, copy lighting or pose, big shapes only, avoid copying text/logos) and the stack encoder builds Krea conditioning with per-card strength, per-layer gains, and guarded prompt handling."
|
||||
"description": "Give each reference image a job, and every attribute a dial. Guide cards assign per-image roles (keep the same subject, suggest the visual style, copy lighting or pose, big shapes only, avoid copying text/logos) with per-card strength, timing, and user-defined recipes - and Concept Slider cards turn any attribute you can name (brightness, age, fog density) into a training-free -6..+6 dial derived from Krea 2 text encoding."
|
||||
},
|
||||
{
|
||||
"author": "DutchyDutch",
|
||||
|
||||
@ -752,7 +752,6 @@
|
||||
"GrsaiFluxKontext_MultiImageToImage",
|
||||
"GrsaiFluxKontext_TextToImage",
|
||||
"Grsai_GPTImage",
|
||||
"Grsai_GPTImageVIP",
|
||||
"Grsai_NanoBanana",
|
||||
"Grsai_NanoBanana2",
|
||||
"Grsai_NanoBananaPro"
|
||||
@ -3988,6 +3987,9 @@
|
||||
{
|
||||
"author": "Azornes",
|
||||
"description": "Logging Configuration",
|
||||
"preemptions": [
|
||||
"ResolutionMaster"
|
||||
],
|
||||
"title": "AzLogs",
|
||||
"title_aux": "Comfyui-Resolution-Master"
|
||||
}
|
||||
@ -17719,6 +17721,18 @@
|
||||
"title_aux": "ComfyUI-Montagen"
|
||||
}
|
||||
],
|
||||
"https://github.com/Moon-NE/MoonNe_Nodes": [
|
||||
[
|
||||
"BatchConditionalText",
|
||||
"BatchConditionalTextWeight",
|
||||
"Moon_NE_Text_Concatenate",
|
||||
"TextHorizontalToVertical",
|
||||
"batch_randoml_text"
|
||||
],
|
||||
{
|
||||
"title_aux": "MoonNe_Nodes"
|
||||
}
|
||||
],
|
||||
"https://github.com/MoonHugo/ComfyUI-BAGEL-Hugo": [
|
||||
[
|
||||
"BagelByHugo"
|
||||
@ -20976,223 +20990,6 @@
|
||||
"title_aux": "ComfyUI-PromptUtils"
|
||||
}
|
||||
],
|
||||
"https://github.com/Runware/ComfyUI-Runware": [
|
||||
[
|
||||
"Runware 3D Inference",
|
||||
"Runware 3D Inference Inputs",
|
||||
"Runware 3D Inference Settings",
|
||||
"Runware 3D Inference Settings Draco Compression",
|
||||
"Runware 3D Inference Settings Mesh Cluster",
|
||||
"Runware 3D Inference Settings Shape Slat",
|
||||
"Runware 3D Inference Settings Sparse Structure",
|
||||
"Runware 3D Inference Settings Tex Slat",
|
||||
"Runware 3D Model Search",
|
||||
"Runware API Manager",
|
||||
"Runware Accelerator Options",
|
||||
"Runware Alibaba Provider Settings",
|
||||
"Runware Audio Inference",
|
||||
"Runware Audio Inference Inputs",
|
||||
"Runware Audio Inference Inputs Reference Audio",
|
||||
"Runware Audio Inference Settings",
|
||||
"Runware Audio Inference Settings Voice Modify",
|
||||
"Runware Audio Inference Speech",
|
||||
"Runware Audio Inference Speech Voices",
|
||||
"Runware Audio Model Search",
|
||||
"Runware Audio Sections",
|
||||
"Runware Background Removal",
|
||||
"Runware BlackForest Labs Provider Settings",
|
||||
"Runware Bria Provider Mask",
|
||||
"Runware Bria Provider Settings",
|
||||
"Runware Bytedance Provider Settings",
|
||||
"Runware ControlNet",
|
||||
"Runware ControlNet Combine",
|
||||
"Runware ControlNet PreProcessor",
|
||||
"Runware ElevenLabs Provider Settings",
|
||||
"Runware ElevenLabs Provider Settings Sections",
|
||||
"Runware Embedding Search",
|
||||
"Runware Embeddings Combine",
|
||||
"Runware Frame Images",
|
||||
"Runware Google Provider Settings",
|
||||
"Runware IPAdapter",
|
||||
"Runware IPAdapters Combine",
|
||||
"Runware Image Advanced Feature Input",
|
||||
"Runware Image Caption",
|
||||
"Runware Image Inference",
|
||||
"Runware Image Inference Inputs",
|
||||
"Runware Image Inference Inputs Fonts",
|
||||
"Runware Image Inference Settings Color Palette",
|
||||
"Runware Image Inference Settings Moodboards",
|
||||
"Runware Image Inference Settings Prompt Enhance",
|
||||
"Runware Image Inference Settings Scoring Rubric",
|
||||
"Runware Image Inference Settings Scoring Rubric Combine",
|
||||
"Runware Image Inference Settings Structured Prompt",
|
||||
"Runware Image Inference Ultralytics",
|
||||
"Runware Image Masking",
|
||||
"Runware Image Upscaler",
|
||||
"Runware Image Upscaler Settings",
|
||||
"Runware Imagen Inference",
|
||||
"Runware Input Audios",
|
||||
"Runware Kling MultiPrompt Segment",
|
||||
"Runware Kling Provider Settings MultiPrompt",
|
||||
"Runware KlingAI Provider Settings",
|
||||
"Runware Kontext Inference",
|
||||
"Runware Lightricks Provider Settings",
|
||||
"Runware Load Mesh",
|
||||
"Runware Lora Combine",
|
||||
"Runware Lora Search",
|
||||
"Runware Luma Provider Settings",
|
||||
"Runware Media Upload",
|
||||
"Runware Midjourney Provider Settings",
|
||||
"Runware MiniMax Provider Settings",
|
||||
"Runware Mirelo Provider Settings",
|
||||
"Runware Model Search",
|
||||
"Runware Multi Inference",
|
||||
"Runware OpenAI Provider Settings",
|
||||
"Runware Outpaint",
|
||||
"Runware PhotoMaker V2",
|
||||
"Runware Pixverse Provider Settings",
|
||||
"Runware Provider Settings Recraft Color",
|
||||
"Runware Recraft Provider Settings",
|
||||
"Runware Reference Audios",
|
||||
"Runware Reference Images",
|
||||
"Runware Reference Videos",
|
||||
"Runware Reference Voices",
|
||||
"Runware Refiner",
|
||||
"Runware Regional Prompting Advanced Feature",
|
||||
"Runware Regional Prompting Advanced Feature Regions",
|
||||
"Runware Runway Provider Settings",
|
||||
"Runware Safety Inputs",
|
||||
"Runware Save 3D",
|
||||
"Runware Save Image",
|
||||
"Runware Save Text",
|
||||
"Runware Settings",
|
||||
"Runware Sourceful Provider Settings",
|
||||
"Runware Sourceful Provider Settings Fonts",
|
||||
"Runware Speech Inputs Combine",
|
||||
"Runware Sync Provider Settings",
|
||||
"Runware Sync Segment",
|
||||
"Runware Text Inference",
|
||||
"Runware Text Inference Inputs",
|
||||
"Runware Text Inference Inputs Images",
|
||||
"Runware Text Inference Inputs Videos",
|
||||
"Runware Text Inference Message",
|
||||
"Runware Text Inference Messages",
|
||||
"Runware Text Inference Settings",
|
||||
"Runware Text Inference Settings Cache",
|
||||
"Runware Text Inference Tool Choice",
|
||||
"Runware Text Model Search",
|
||||
"Runware VAE Search",
|
||||
"Runware Vectorize",
|
||||
"Runware Video Advanced Feature Inputs",
|
||||
"Runware Video Audio Input",
|
||||
"Runware Video Background Removal",
|
||||
"Runware Video Inference",
|
||||
"Runware Video Inference Elements",
|
||||
"Runware Video Inference Elements Combine",
|
||||
"Runware Video Inference Inputs",
|
||||
"Runware Video Inference Inputs Reference Audios",
|
||||
"Runware Video Inference Inputs Reference Images",
|
||||
"Runware Video Inference Inputs Reference Images Multiple Images Connector",
|
||||
"Runware Video Inference Inputs Reference Videos",
|
||||
"Runware Video Inference Inputs Reference Voices",
|
||||
"Runware Video Inference Lora",
|
||||
"Runware Video Inference Outputs",
|
||||
"Runware Video Inference Settings",
|
||||
"Runware Video Inference Settings Active Speaker Bounding Boxes",
|
||||
"Runware Video Inference Settings Active Speaker Detection",
|
||||
"Runware Video Inference Settings Edit",
|
||||
"Runware Video Inference Settings Segments",
|
||||
"Runware Video Inference Settings Source Position",
|
||||
"Runware Video Inference Settings TTS",
|
||||
"Runware Video Inference Speech Input",
|
||||
"Runware Video Inputs Frame Images",
|
||||
"Runware Video Inputs References",
|
||||
"Runware Video Model Search",
|
||||
"Runware Video Speech Input",
|
||||
"Runware Video Transcription",
|
||||
"Runware Video Upscaler",
|
||||
"Runware Vidu Provider Settings",
|
||||
"Runware Wan Animate Advanced Feature Settings",
|
||||
"Runware Watermark Advanced Feature",
|
||||
"Runware xAI Provider Settings",
|
||||
"Runware3DInference",
|
||||
"Runware3DInferenceInputs",
|
||||
"Runware3DInferenceSettings",
|
||||
"Runware3DInferenceSettingsDracoCompression",
|
||||
"Runware3DInferenceSettingsMeshCluster",
|
||||
"Runware3DInferenceSettingsShapeSlat",
|
||||
"Runware3DInferenceSettingsSparseStructure",
|
||||
"Runware3DInferenceSettingsTexSlat",
|
||||
"RunwareAlibabaProviderSettings",
|
||||
"RunwareAudioInferenceInputs",
|
||||
"RunwareAudioInferenceReferenceVoices",
|
||||
"RunwareAudioInferenceSpeech",
|
||||
"RunwareAudioInferenceSpeechVoices",
|
||||
"RunwareAudioInput",
|
||||
"RunwareAudioSettings",
|
||||
"RunwareAudioSettingsVoiceModify",
|
||||
"RunwareBlackForestProviderSettings",
|
||||
"RunwareBriaProviderMask",
|
||||
"RunwareBriaProviderSettings",
|
||||
"RunwareBytedanceProviderSettings",
|
||||
"RunwareFrameImages",
|
||||
"RunwareGoogleProviderSettings",
|
||||
"RunwareImageInferenceAdvancedFeatures",
|
||||
"RunwareImageInferenceSettingsPromptEnhance",
|
||||
"RunwareImageInferenceSettingsStructuredPrompt",
|
||||
"RunwareKlingMultiPromptSegment",
|
||||
"RunwareKlingProviderSettings",
|
||||
"RunwareKlingProviderSettingsMultiPrompt",
|
||||
"RunwareLightricksProviderSettings",
|
||||
"RunwareLoadMesh",
|
||||
"RunwareLumaProviderSettings",
|
||||
"RunwareMidjourneyProviderSettings",
|
||||
"RunwareMiniMaxProviderSettings",
|
||||
"RunwareMireloProviderSettings",
|
||||
"RunwareOpenAIProviderSettings",
|
||||
"RunwarePixverseProviderSettings",
|
||||
"RunwareRecraftColor",
|
||||
"RunwareRecraftProviderSettings",
|
||||
"RunwareRegionalPromptingAdvancedFeature",
|
||||
"RunwareRegionalPromptingAdvancedFeatureRegions",
|
||||
"RunwareRunwayProviderSettings",
|
||||
"RunwareSafetyInputs",
|
||||
"RunwareSave3D",
|
||||
"RunwareSourcefulProviderSettings",
|
||||
"RunwareSourcefulProviderSettingsFonts",
|
||||
"RunwareSpeechInput",
|
||||
"RunwareSpeechInputCombine",
|
||||
"RunwareSyncProviderSettings",
|
||||
"RunwareSyncSegment",
|
||||
"RunwareTextInferenceSettingsCache",
|
||||
"RunwareTextInferenceToolChoice",
|
||||
"RunwareUltralyticsInputs",
|
||||
"RunwareUltralyticsProviderSettings",
|
||||
"RunwareVectorize",
|
||||
"RunwareVideoAdvancedFeatureInputs",
|
||||
"RunwareVideoBgRemoval",
|
||||
"RunwareVideoInferenceOutputs",
|
||||
"RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes",
|
||||
"RunwareVideoInferenceSettingsActiveSpeakerDetection",
|
||||
"RunwareVideoInferenceSettingsControl",
|
||||
"RunwareVideoInferenceSettingsSegments",
|
||||
"RunwareVideoInferenceSettingsSourcePosition",
|
||||
"RunwareVideoInferenceSettingsTts",
|
||||
"RunwareVideoInferenceSpeechInput",
|
||||
"RunwareVideoInputsFrameImages",
|
||||
"RunwareVideoInputsReferenceImagesMultipleImagesConnector",
|
||||
"RunwareVideoInputsReferences",
|
||||
"RunwareVideoSettings",
|
||||
"RunwareVideoUpscaler",
|
||||
"RunwareWanAnimateAdvancedFeatureSettings",
|
||||
"RunwareWatermarkAdvancedFeature",
|
||||
"RunwareXAIProviderSettings",
|
||||
"txt2vid"
|
||||
],
|
||||
{
|
||||
"title_aux": "Runware.ai ComfyUI Inference API Integration"
|
||||
}
|
||||
],
|
||||
"https://github.com/RyanHolanda/ComfyUI-AudioBridge": [
|
||||
[
|
||||
"AudioToTensor",
|
||||
@ -25444,6 +25241,26 @@
|
||||
"title_aux": "ComfyUI-RotoMask"
|
||||
}
|
||||
],
|
||||
"https://github.com/TechnoWarrior2/comfyui-image-picker": [
|
||||
[
|
||||
"AlexImagePicker"
|
||||
],
|
||||
{
|
||||
"author": "alex",
|
||||
"description": "Pauses workflow and opens a new browser window to pick which images to pass through.",
|
||||
"nickname": "Image Picker",
|
||||
"title": "Alex Image Picker",
|
||||
"title_aux": "ComfyUI Image Picker"
|
||||
}
|
||||
],
|
||||
"https://github.com/TechnoWarrior2/comfyui-live-preview": [
|
||||
[
|
||||
"AlexLivePreview"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI Live Preview"
|
||||
}
|
||||
],
|
||||
"https://github.com/TemryL/ComfyS3": [
|
||||
[
|
||||
"DownloadFileS3",
|
||||
@ -48485,7 +48302,8 @@
|
||||
],
|
||||
"https://github.com/jtydhr88/ComfyUI-qwenmultiangle": [
|
||||
[
|
||||
"QwenMultiangleCameraNode"
|
||||
"QwenMultiangleCameraNode",
|
||||
"QwenMultiangleCameraTranslateNode"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI-qwenmultiangle"
|
||||
@ -64003,6 +63821,22 @@
|
||||
"title_aux": "comfyui-clippy-reloaded"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-image-of-the-day": [
|
||||
[
|
||||
"ImageOfDayLoader"
|
||||
],
|
||||
{
|
||||
"title_aux": "Image of the Day"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-lora-FindingLora": [
|
||||
[
|
||||
"LoraLoaderFindingLora"
|
||||
],
|
||||
{
|
||||
"title_aux": "Finding LoRA"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-model-diff-to-lora": [
|
||||
[
|
||||
"ModelDiffToLoRA"
|
||||
@ -72296,6 +72130,19 @@
|
||||
"title_aux": "ComfyUI-KYNode"
|
||||
}
|
||||
],
|
||||
"https://github.com/younestft/Comfy_EverAnimate": [
|
||||
[
|
||||
"ComfyEverAnimate",
|
||||
"ComfyEverAnimateChunksCalculator",
|
||||
"ComfyEverAnimateColorCorrection",
|
||||
"ComfyEverAnimateContinueChunk",
|
||||
"ComfyEverAnimateInitialChunk",
|
||||
"ComfyEverAnimateMasterSettings"
|
||||
],
|
||||
{
|
||||
"title_aux": "Comfy EverAnimate"
|
||||
}
|
||||
],
|
||||
"https://github.com/younger15/ComfyUI-IterSwitch": [
|
||||
[
|
||||
"Iter_CLIPSwitch",
|
||||
|
||||
7156
github-stats.json
7156
github-stats.json
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
@ -1974,7 +1985,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")
|
||||
@ -1986,6 +1997,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
|
||||
@ -1996,6 +2014,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('#'):
|
||||
|
||||
@ -517,7 +517,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)
|
||||
@ -526,7 +531,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']:
|
||||
@ -1421,7 +1426,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)
|
||||
@ -1521,6 +1528,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'):
|
||||
|
||||
@ -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("");
|
||||
|
||||
@ -1511,6 +2053,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';
|
||||
|
||||
@ -4970,9 +4970,13 @@
|
||||
"SV-ClearCustomCaches",
|
||||
"SV-CompressConds",
|
||||
"SV-CondDiffSharpening",
|
||||
"SV-ConditioningArithmetic",
|
||||
"SV-ConditioningInterferenceSchedule",
|
||||
"SV-ConditioningMaskMerge",
|
||||
"SV-ConditioningNoiseNudge",
|
||||
"SV-ConditioningReroute",
|
||||
"SV-ConditioningScaleJitter",
|
||||
"SV-ConditioningScheduledBlend",
|
||||
"SV-ConditioningTokenDropoutNudge",
|
||||
"SV-ConsolePrint",
|
||||
"SV-ConsolePrintLoop",
|
||||
@ -5013,16 +5017,15 @@
|
||||
"SV-HashModel",
|
||||
"SV-HashObject",
|
||||
"SV-IfBranch",
|
||||
"SV-ImageInt",
|
||||
"SV-ImageReroute",
|
||||
"SV-InputSelect",
|
||||
"SV-InputSelectBoolean",
|
||||
"SV-InputSelectCompare",
|
||||
"SV-IntMathOperation",
|
||||
"SV-Krea2LocalStyleReferenceOld",
|
||||
"SV-Krea2Reference",
|
||||
"SV-Krea2ReferenceAdvanced",
|
||||
"SV-Krea2ReferencePlus",
|
||||
"SV-Krea2ResizeImage",
|
||||
"SV-LLMArgs",
|
||||
"SV-LLMRequest",
|
||||
"SV-ListToAccumulationNode",
|
||||
@ -5044,6 +5047,7 @@
|
||||
"SV-PrecisionFloat",
|
||||
"SV-PrimitiveFloat",
|
||||
"SV-PrimitiveNone",
|
||||
"SV-PromptGeneratorAPI",
|
||||
"SV-PromptPlusModel",
|
||||
"SV-PromptPlusModelOutput",
|
||||
"SV-PromptProcessing",
|
||||
@ -5056,11 +5060,14 @@
|
||||
"SV-PromptProcessingSimple",
|
||||
"SV-PromptProcessingVars",
|
||||
"SV-RandomImage",
|
||||
"SV-RegexMatch",
|
||||
"SV-RegexReplace",
|
||||
"SV-ResizeImage",
|
||||
"SV-ResolutionSelector",
|
||||
"SV-ResolutionSelector2",
|
||||
"SV-ResolutionSelector2Output",
|
||||
"SV-SamplerNameToSampler",
|
||||
"SV-SaveImageCustom",
|
||||
"SV-SaveTextFile",
|
||||
"SV-ScaleConditioning",
|
||||
"SV-SigmaAsFloat",
|
||||
@ -5077,6 +5084,7 @@
|
||||
"SV-SigmaReverse",
|
||||
"SV-SigmaStartEnd",
|
||||
"SV-SigmaStrength",
|
||||
"SV-SimpleString",
|
||||
"SV-SimpleText",
|
||||
"SV-SkimmedCFGDifference",
|
||||
"SV-SkimmedCFGDual",
|
||||
@ -5096,7 +5104,8 @@
|
||||
"SV-WhileLoopOpen",
|
||||
"SV-WildcardLoader",
|
||||
"SV-WildcardProcessing",
|
||||
"SV-WildcardString"
|
||||
"SV-WildcardString",
|
||||
"SV-WorkflowAgent"
|
||||
],
|
||||
{
|
||||
"title_aux": "SirVeggie/Custom nodes for ComfyUI"
|
||||
|
||||
@ -752,7 +752,6 @@
|
||||
"GrsaiFluxKontext_MultiImageToImage",
|
||||
"GrsaiFluxKontext_TextToImage",
|
||||
"Grsai_GPTImage",
|
||||
"Grsai_GPTImageVIP",
|
||||
"Grsai_NanoBanana",
|
||||
"Grsai_NanoBanana2",
|
||||
"Grsai_NanoBananaPro"
|
||||
@ -3988,6 +3987,9 @@
|
||||
{
|
||||
"author": "Azornes",
|
||||
"description": "Logging Configuration",
|
||||
"preemptions": [
|
||||
"ResolutionMaster"
|
||||
],
|
||||
"title": "AzLogs",
|
||||
"title_aux": "Comfyui-Resolution-Master"
|
||||
}
|
||||
@ -17719,6 +17721,18 @@
|
||||
"title_aux": "ComfyUI-Montagen"
|
||||
}
|
||||
],
|
||||
"https://github.com/Moon-NE/MoonNe_Nodes": [
|
||||
[
|
||||
"BatchConditionalText",
|
||||
"BatchConditionalTextWeight",
|
||||
"Moon_NE_Text_Concatenate",
|
||||
"TextHorizontalToVertical",
|
||||
"batch_randoml_text"
|
||||
],
|
||||
{
|
||||
"title_aux": "MoonNe_Nodes"
|
||||
}
|
||||
],
|
||||
"https://github.com/MoonHugo/ComfyUI-BAGEL-Hugo": [
|
||||
[
|
||||
"BagelByHugo"
|
||||
@ -20976,223 +20990,6 @@
|
||||
"title_aux": "ComfyUI-PromptUtils"
|
||||
}
|
||||
],
|
||||
"https://github.com/Runware/ComfyUI-Runware": [
|
||||
[
|
||||
"Runware 3D Inference",
|
||||
"Runware 3D Inference Inputs",
|
||||
"Runware 3D Inference Settings",
|
||||
"Runware 3D Inference Settings Draco Compression",
|
||||
"Runware 3D Inference Settings Mesh Cluster",
|
||||
"Runware 3D Inference Settings Shape Slat",
|
||||
"Runware 3D Inference Settings Sparse Structure",
|
||||
"Runware 3D Inference Settings Tex Slat",
|
||||
"Runware 3D Model Search",
|
||||
"Runware API Manager",
|
||||
"Runware Accelerator Options",
|
||||
"Runware Alibaba Provider Settings",
|
||||
"Runware Audio Inference",
|
||||
"Runware Audio Inference Inputs",
|
||||
"Runware Audio Inference Inputs Reference Audio",
|
||||
"Runware Audio Inference Settings",
|
||||
"Runware Audio Inference Settings Voice Modify",
|
||||
"Runware Audio Inference Speech",
|
||||
"Runware Audio Inference Speech Voices",
|
||||
"Runware Audio Model Search",
|
||||
"Runware Audio Sections",
|
||||
"Runware Background Removal",
|
||||
"Runware BlackForest Labs Provider Settings",
|
||||
"Runware Bria Provider Mask",
|
||||
"Runware Bria Provider Settings",
|
||||
"Runware Bytedance Provider Settings",
|
||||
"Runware ControlNet",
|
||||
"Runware ControlNet Combine",
|
||||
"Runware ControlNet PreProcessor",
|
||||
"Runware ElevenLabs Provider Settings",
|
||||
"Runware ElevenLabs Provider Settings Sections",
|
||||
"Runware Embedding Search",
|
||||
"Runware Embeddings Combine",
|
||||
"Runware Frame Images",
|
||||
"Runware Google Provider Settings",
|
||||
"Runware IPAdapter",
|
||||
"Runware IPAdapters Combine",
|
||||
"Runware Image Advanced Feature Input",
|
||||
"Runware Image Caption",
|
||||
"Runware Image Inference",
|
||||
"Runware Image Inference Inputs",
|
||||
"Runware Image Inference Inputs Fonts",
|
||||
"Runware Image Inference Settings Color Palette",
|
||||
"Runware Image Inference Settings Moodboards",
|
||||
"Runware Image Inference Settings Prompt Enhance",
|
||||
"Runware Image Inference Settings Scoring Rubric",
|
||||
"Runware Image Inference Settings Scoring Rubric Combine",
|
||||
"Runware Image Inference Settings Structured Prompt",
|
||||
"Runware Image Inference Ultralytics",
|
||||
"Runware Image Masking",
|
||||
"Runware Image Upscaler",
|
||||
"Runware Image Upscaler Settings",
|
||||
"Runware Imagen Inference",
|
||||
"Runware Input Audios",
|
||||
"Runware Kling MultiPrompt Segment",
|
||||
"Runware Kling Provider Settings MultiPrompt",
|
||||
"Runware KlingAI Provider Settings",
|
||||
"Runware Kontext Inference",
|
||||
"Runware Lightricks Provider Settings",
|
||||
"Runware Load Mesh",
|
||||
"Runware Lora Combine",
|
||||
"Runware Lora Search",
|
||||
"Runware Luma Provider Settings",
|
||||
"Runware Media Upload",
|
||||
"Runware Midjourney Provider Settings",
|
||||
"Runware MiniMax Provider Settings",
|
||||
"Runware Mirelo Provider Settings",
|
||||
"Runware Model Search",
|
||||
"Runware Multi Inference",
|
||||
"Runware OpenAI Provider Settings",
|
||||
"Runware Outpaint",
|
||||
"Runware PhotoMaker V2",
|
||||
"Runware Pixverse Provider Settings",
|
||||
"Runware Provider Settings Recraft Color",
|
||||
"Runware Recraft Provider Settings",
|
||||
"Runware Reference Audios",
|
||||
"Runware Reference Images",
|
||||
"Runware Reference Videos",
|
||||
"Runware Reference Voices",
|
||||
"Runware Refiner",
|
||||
"Runware Regional Prompting Advanced Feature",
|
||||
"Runware Regional Prompting Advanced Feature Regions",
|
||||
"Runware Runway Provider Settings",
|
||||
"Runware Safety Inputs",
|
||||
"Runware Save 3D",
|
||||
"Runware Save Image",
|
||||
"Runware Save Text",
|
||||
"Runware Settings",
|
||||
"Runware Sourceful Provider Settings",
|
||||
"Runware Sourceful Provider Settings Fonts",
|
||||
"Runware Speech Inputs Combine",
|
||||
"Runware Sync Provider Settings",
|
||||
"Runware Sync Segment",
|
||||
"Runware Text Inference",
|
||||
"Runware Text Inference Inputs",
|
||||
"Runware Text Inference Inputs Images",
|
||||
"Runware Text Inference Inputs Videos",
|
||||
"Runware Text Inference Message",
|
||||
"Runware Text Inference Messages",
|
||||
"Runware Text Inference Settings",
|
||||
"Runware Text Inference Settings Cache",
|
||||
"Runware Text Inference Tool Choice",
|
||||
"Runware Text Model Search",
|
||||
"Runware VAE Search",
|
||||
"Runware Vectorize",
|
||||
"Runware Video Advanced Feature Inputs",
|
||||
"Runware Video Audio Input",
|
||||
"Runware Video Background Removal",
|
||||
"Runware Video Inference",
|
||||
"Runware Video Inference Elements",
|
||||
"Runware Video Inference Elements Combine",
|
||||
"Runware Video Inference Inputs",
|
||||
"Runware Video Inference Inputs Reference Audios",
|
||||
"Runware Video Inference Inputs Reference Images",
|
||||
"Runware Video Inference Inputs Reference Images Multiple Images Connector",
|
||||
"Runware Video Inference Inputs Reference Videos",
|
||||
"Runware Video Inference Inputs Reference Voices",
|
||||
"Runware Video Inference Lora",
|
||||
"Runware Video Inference Outputs",
|
||||
"Runware Video Inference Settings",
|
||||
"Runware Video Inference Settings Active Speaker Bounding Boxes",
|
||||
"Runware Video Inference Settings Active Speaker Detection",
|
||||
"Runware Video Inference Settings Edit",
|
||||
"Runware Video Inference Settings Segments",
|
||||
"Runware Video Inference Settings Source Position",
|
||||
"Runware Video Inference Settings TTS",
|
||||
"Runware Video Inference Speech Input",
|
||||
"Runware Video Inputs Frame Images",
|
||||
"Runware Video Inputs References",
|
||||
"Runware Video Model Search",
|
||||
"Runware Video Speech Input",
|
||||
"Runware Video Transcription",
|
||||
"Runware Video Upscaler",
|
||||
"Runware Vidu Provider Settings",
|
||||
"Runware Wan Animate Advanced Feature Settings",
|
||||
"Runware Watermark Advanced Feature",
|
||||
"Runware xAI Provider Settings",
|
||||
"Runware3DInference",
|
||||
"Runware3DInferenceInputs",
|
||||
"Runware3DInferenceSettings",
|
||||
"Runware3DInferenceSettingsDracoCompression",
|
||||
"Runware3DInferenceSettingsMeshCluster",
|
||||
"Runware3DInferenceSettingsShapeSlat",
|
||||
"Runware3DInferenceSettingsSparseStructure",
|
||||
"Runware3DInferenceSettingsTexSlat",
|
||||
"RunwareAlibabaProviderSettings",
|
||||
"RunwareAudioInferenceInputs",
|
||||
"RunwareAudioInferenceReferenceVoices",
|
||||
"RunwareAudioInferenceSpeech",
|
||||
"RunwareAudioInferenceSpeechVoices",
|
||||
"RunwareAudioInput",
|
||||
"RunwareAudioSettings",
|
||||
"RunwareAudioSettingsVoiceModify",
|
||||
"RunwareBlackForestProviderSettings",
|
||||
"RunwareBriaProviderMask",
|
||||
"RunwareBriaProviderSettings",
|
||||
"RunwareBytedanceProviderSettings",
|
||||
"RunwareFrameImages",
|
||||
"RunwareGoogleProviderSettings",
|
||||
"RunwareImageInferenceAdvancedFeatures",
|
||||
"RunwareImageInferenceSettingsPromptEnhance",
|
||||
"RunwareImageInferenceSettingsStructuredPrompt",
|
||||
"RunwareKlingMultiPromptSegment",
|
||||
"RunwareKlingProviderSettings",
|
||||
"RunwareKlingProviderSettingsMultiPrompt",
|
||||
"RunwareLightricksProviderSettings",
|
||||
"RunwareLoadMesh",
|
||||
"RunwareLumaProviderSettings",
|
||||
"RunwareMidjourneyProviderSettings",
|
||||
"RunwareMiniMaxProviderSettings",
|
||||
"RunwareMireloProviderSettings",
|
||||
"RunwareOpenAIProviderSettings",
|
||||
"RunwarePixverseProviderSettings",
|
||||
"RunwareRecraftColor",
|
||||
"RunwareRecraftProviderSettings",
|
||||
"RunwareRegionalPromptingAdvancedFeature",
|
||||
"RunwareRegionalPromptingAdvancedFeatureRegions",
|
||||
"RunwareRunwayProviderSettings",
|
||||
"RunwareSafetyInputs",
|
||||
"RunwareSave3D",
|
||||
"RunwareSourcefulProviderSettings",
|
||||
"RunwareSourcefulProviderSettingsFonts",
|
||||
"RunwareSpeechInput",
|
||||
"RunwareSpeechInputCombine",
|
||||
"RunwareSyncProviderSettings",
|
||||
"RunwareSyncSegment",
|
||||
"RunwareTextInferenceSettingsCache",
|
||||
"RunwareTextInferenceToolChoice",
|
||||
"RunwareUltralyticsInputs",
|
||||
"RunwareUltralyticsProviderSettings",
|
||||
"RunwareVectorize",
|
||||
"RunwareVideoAdvancedFeatureInputs",
|
||||
"RunwareVideoBgRemoval",
|
||||
"RunwareVideoInferenceOutputs",
|
||||
"RunwareVideoInferenceSettingsActiveSpeakerBoundingBoxes",
|
||||
"RunwareVideoInferenceSettingsActiveSpeakerDetection",
|
||||
"RunwareVideoInferenceSettingsControl",
|
||||
"RunwareVideoInferenceSettingsSegments",
|
||||
"RunwareVideoInferenceSettingsSourcePosition",
|
||||
"RunwareVideoInferenceSettingsTts",
|
||||
"RunwareVideoInferenceSpeechInput",
|
||||
"RunwareVideoInputsFrameImages",
|
||||
"RunwareVideoInputsReferenceImagesMultipleImagesConnector",
|
||||
"RunwareVideoInputsReferences",
|
||||
"RunwareVideoSettings",
|
||||
"RunwareVideoUpscaler",
|
||||
"RunwareWanAnimateAdvancedFeatureSettings",
|
||||
"RunwareWatermarkAdvancedFeature",
|
||||
"RunwareXAIProviderSettings",
|
||||
"txt2vid"
|
||||
],
|
||||
{
|
||||
"title_aux": "Runware.ai ComfyUI Inference API Integration"
|
||||
}
|
||||
],
|
||||
"https://github.com/RyanHolanda/ComfyUI-AudioBridge": [
|
||||
[
|
||||
"AudioToTensor",
|
||||
@ -25444,6 +25241,26 @@
|
||||
"title_aux": "ComfyUI-RotoMask"
|
||||
}
|
||||
],
|
||||
"https://github.com/TechnoWarrior2/comfyui-image-picker": [
|
||||
[
|
||||
"AlexImagePicker"
|
||||
],
|
||||
{
|
||||
"author": "alex",
|
||||
"description": "Pauses workflow and opens a new browser window to pick which images to pass through.",
|
||||
"nickname": "Image Picker",
|
||||
"title": "Alex Image Picker",
|
||||
"title_aux": "ComfyUI Image Picker"
|
||||
}
|
||||
],
|
||||
"https://github.com/TechnoWarrior2/comfyui-live-preview": [
|
||||
[
|
||||
"AlexLivePreview"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI Live Preview"
|
||||
}
|
||||
],
|
||||
"https://github.com/TemryL/ComfyS3": [
|
||||
[
|
||||
"DownloadFileS3",
|
||||
@ -48485,7 +48302,8 @@
|
||||
],
|
||||
"https://github.com/jtydhr88/ComfyUI-qwenmultiangle": [
|
||||
[
|
||||
"QwenMultiangleCameraNode"
|
||||
"QwenMultiangleCameraNode",
|
||||
"QwenMultiangleCameraTranslateNode"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI-qwenmultiangle"
|
||||
@ -64003,6 +63821,22 @@
|
||||
"title_aux": "comfyui-clippy-reloaded"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-image-of-the-day": [
|
||||
[
|
||||
"ImageOfDayLoader"
|
||||
],
|
||||
{
|
||||
"title_aux": "Image of the Day"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-lora-FindingLora": [
|
||||
[
|
||||
"LoraLoaderFindingLora"
|
||||
],
|
||||
{
|
||||
"title_aux": "Finding LoRA"
|
||||
}
|
||||
],
|
||||
"https://github.com/shootthesound/comfyui-model-diff-to-lora": [
|
||||
[
|
||||
"ModelDiffToLoRA"
|
||||
@ -72296,6 +72130,19 @@
|
||||
"title_aux": "ComfyUI-KYNode"
|
||||
}
|
||||
],
|
||||
"https://github.com/younestft/Comfy_EverAnimate": [
|
||||
[
|
||||
"ComfyEverAnimate",
|
||||
"ComfyEverAnimateChunksCalculator",
|
||||
"ComfyEverAnimateColorCorrection",
|
||||
"ComfyEverAnimateContinueChunk",
|
||||
"ComfyEverAnimateInitialChunk",
|
||||
"ComfyEverAnimateMasterSettings"
|
||||
],
|
||||
{
|
||||
"title_aux": "Comfy EverAnimate"
|
||||
}
|
||||
],
|
||||
"https://github.com/younger15/ComfyUI-IterSwitch": [
|
||||
[
|
||||
"Iter_CLIPSwitch",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user