├── .github
└── workflows
│ └── publish.yml
├── .gitignore
├── AiHelper.py
├── Comfly.py
├── Comflyapi.json
├── LICENSE
├── README.md
├── __init__.py
├── docs
└── mjstyle
│ ├── mj_art.json
│ └── mj_hd.json
├── pyproject.toml
├── requirements.txt
├── utils.py
├── web
└── js
│ ├── Comfly_kling_videoPreview.js
│ ├── Comfly_manager.js
│ ├── Comfly_mjstyle.js
│ ├── chat_button.js
│ ├── comfly.js
│ ├── drag_handle.js
│ ├── help_button.js
│ └── split_image.js
└── workflow
├── Comfly Doubao SeedEdit.json
├── ComflyGeminiAPI.json
├── Comfly_lip_sync.json
├── Comfly_mj.json
├── comfly-gemmi-editimage.json
├── comfly-gpt4o-api.json
├── comfly-kling-V1.0-text2video.json
├── comfly-kling-image2video.json
└── comfly-kling-text2video.json
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Comfy registry
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - main
7 | - master
8 | paths:
9 | - "pyproject.toml"
10 |
11 | permissions:
12 | issues: write
13 |
14 | jobs:
15 | publish-node:
16 | name: Publish Custom Node to registry
17 | runs-on: ubuntu-latest
18 | if: ${{ github.repository_owner == 'ainewsto' }}
19 | steps:
20 | - name: Check out code
21 | uses: actions/checkout@v4
22 | with:
23 | submodules: true
24 | - name: Publish Custom Node
25 | uses: Comfy-Org/publish-node-action@v1
26 | with:
27 | ## Add your own personal access token to your Github Repository secrets and reference it here.
28 | personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # API key
2 | Comflyapi.json merge=ours
3 |
4 | # Byte-compiled / optimized / DLL files
5 | __pycache__/
6 | *.py[cod]
7 | *$py.class
8 |
9 | # C extensions
10 | *.so
11 |
12 | # Distribution / packaging
13 | .Python
14 | build/
15 | develop-eggs/
16 | dist/
17 | downloads/
18 | eggs/
19 | .eggs/
20 | lib/
21 | lib64/
22 | parts/
23 | sdist/
24 | var/
25 | wheels/
26 | share/python-wheels/
27 | *.egg-info/
28 | .installed.cfg
29 | *.egg
30 | MANIFEST
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .nox/
46 | .coverage
47 | .coverage.*
48 | .cache
49 | nosetests.xml
50 | coverage.xml
51 | *.cover
52 | *.py,cover
53 | .hypothesis/
54 | .pytest_cache/
55 | cover/
56 |
57 | # Translations
58 | *.mo
59 | *.pot
60 |
61 | # Django stuff:
62 | *.log
63 | local_settings.py
64 | db.sqlite3
65 | db.sqlite3-journal
66 |
67 | # Flask stuff:
68 | instance/
69 | .webassets-cache
70 |
71 | # Scrapy stuff:
72 | .scrapy
73 |
74 | # Sphinx documentation
75 | docs/_build/
76 |
77 | # PyBuilder
78 | .pybuilder/
79 | target/
80 |
81 | # Jupyter Notebook
82 | .ipynb_checkpoints
83 |
84 | # IPython
85 | profile_default/
86 | ipython_config.py
87 |
88 | # pyenv
89 | # For a library or package, you might want to ignore these files since the code is
90 | # intended to run in multiple environments; otherwise, check them in:
91 | # .python-version
92 |
93 | # pipenv
94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
97 | # install all needed dependencies.
98 | #Pipfile.lock
99 |
100 | # UV
101 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
102 | # This is especially recommended for binary packages to ensure reproducibility, and is more
103 | # commonly ignored for libraries.
104 | #uv.lock
105 |
106 | # poetry
107 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108 | # This is especially recommended for binary packages to ensure reproducibility, and is more
109 | # commonly ignored for libraries.
110 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111 | #poetry.lock
112 |
113 | # pdm
114 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115 | #pdm.lock
116 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117 | # in version control.
118 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
119 | .pdm.toml
120 | .pdm-python
121 | .pdm-build/
122 |
123 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
124 | __pypackages__/
125 |
126 | # Celery stuff
127 | celerybeat-schedule
128 | celerybeat.pid
129 |
130 | # SageMath parsed files
131 | *.sage.py
132 |
133 | # Environments
134 | .env
135 | .venv
136 | env/
137 | venv/
138 | ENV/
139 | env.bak/
140 | venv.bak/
141 |
142 | # Spyder project settings
143 | .spyderproject
144 | .spyproject
145 |
146 | # Rope project settings
147 | .ropeproject
148 |
149 | # mkdocs documentation
150 | /site
151 |
152 | # mypy
153 | .mypy_cache/
154 | .dmypy.json
155 | dmypy.json
156 |
157 | # Pyre type checker
158 | .pyre/
159 |
160 | # pytype static type analyzer
161 | .pytype/
162 |
163 | # Cython debug symbols
164 | cython_debug/
165 |
166 | # PyCharm
167 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
168 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
169 | # and can be added to the global gitignore or merged into this file. For a more nuclear
170 | # option (not recommended) you can uncomment the following to ignore the entire idea folder.
171 | #.idea/
172 |
173 | # Ruff stuff:
174 | .ruff_cache/
175 |
176 | # PyPI configuration file
177 | .pypirc
178 |
--------------------------------------------------------------------------------
/AiHelper.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import aiohttp
3 | from aiohttp import web
4 | from aiohttp_cors import setup, ResourceOptions
5 | import subprocess
6 | import os
7 | import json
8 | import shutil
9 | import git
10 | import requests
11 | import threading
12 | import logging
13 | import time
14 | import numpy as np
15 | import torch
16 | from PIL import Image
17 | from io import BytesIO
18 | import re
19 | import sys
20 | import zipfile
21 | import sysconfig
22 | import hashlib
23 | from io import StringIO
24 | from contextlib import redirect_stdout, redirect_stderr
25 | import psutil
26 | import urllib.parse
27 |
28 |
29 | async def on_prepare(request, response):
30 | request.start_time = time.time()
31 |
32 | async def on_response(request, response):
33 | pass
34 |
35 | async def on_request_start(request, *args, **kwargs):
36 | pass
37 |
38 | async def on_request_end(request, *args, **kwargs):
39 | pass
40 |
41 |
42 | async def get_python_path():
43 | current_path = os.path.abspath(__file__)
44 | comfyui_path = os.path.abspath(os.path.join(current_path, "..", "..", ".."))
45 | python_paths = [
46 | os.path.join(comfyui_path, "python_miniconda", "python.exe"),
47 | os.path.join(comfyui_path, "python_miniconda", "bin", "python"),
48 | os.path.join(comfyui_path, "venv", "bin", "python")
49 | ]
50 | for path in python_paths:
51 | if os.path.exists(path):
52 | return path
53 | return "python"
54 |
55 | async def get_plugins_path():
56 | current_path = os.path.dirname(os.path.abspath(__file__))
57 | plugins_dir = os.path.abspath(os.path.join(current_path, ".."))
58 | return plugins_dir
59 |
60 | async def get_plugin_path(plugin_name):
61 | plugins_dir = await get_plugins_path()
62 | plugin_path = os.path.join(plugins_dir, plugin_name)
63 | return plugin_path
64 |
65 | async def get_dependencies(request):
66 | try:
67 | python_path = await get_python_path()
68 | output = subprocess.check_output([python_path, '-m', 'pip', 'list', '--format=json'])
69 | dependencies = json.loads(output)
70 | formatted_dependencies = [f"{dep['name']}=={dep['version']}" for dep in dependencies]
71 | return web.json_response(formatted_dependencies)
72 | except subprocess.CalledProcessError as e:
73 | logging.error(f"Error getting dependencies: {str(e)}")
74 | return web.json_response({'error': str(e)}, status=500)
75 |
76 | async def install_dependency(request):
77 | name = request.query.get('name')
78 | if not name:
79 | return web.json_response({'error': 'Name is required'}, status=400)
80 | try:
81 | python_path = await get_python_path()
82 |
83 | process = subprocess.Popen([python_path, '-m', 'pip', 'install', name], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
84 |
85 | output = []
86 | while True:
87 | line = process.stdout.readline()
88 | if line:
89 | logging.info(line.strip())
90 | output.append(line)
91 | else:
92 | break
93 |
94 | return_code = process.wait()
95 |
96 | if return_code == 0:
97 | response_data = {'message': 'Installation successful', 'output': ''.join(output)}
98 | logging.info(f"Message: {response_data}")
99 | return web.json_response(response_data)
100 | else:
101 | response_data = {'error': 'Installation failed', 'output': ''.join(output)}
102 | logging.error(f"Message: {response_data}")
103 | return web.json_response(response_data, status=500)
104 |
105 | except subprocess.CalledProcessError as e:
106 | logging.error(f"Error installing dependency: {name}")
107 | logging.error(e.output.decode('utf-8'))
108 | return web.json_response({'error': str(e)}, status=500)
109 |
110 |
111 | async def manage_dependency(request):
112 | name = request.query.get('name')
113 | action = request.query.get('action')
114 | if not name or not action:
115 | return web.json_response({'error': 'Name and action are required'}, status=400)
116 | try:
117 | python_path = await get_python_path()
118 | if action == 'uninstall':
119 | subprocess.check_call([python_path, '-m', 'pip', 'uninstall', '-y', name])
120 | else:
121 | return web.json_response({'error': f'Invalid action: {action}'}, status=400)
122 | return web.json_response({'message': f'{action.capitalize()} successful'})
123 | except subprocess.CalledProcessError as e:
124 | logging.error(f"Error {action}ing dependency: {name}")
125 | return web.json_response({'error': str(e)}, status=500)
126 |
127 | async def replace_dependency(request):
128 | name = request.query.get('name')
129 | version = request.query.get('version')
130 | if not name or not version:
131 | return web.json_response({'error': 'Name and version are required'}, status=400)
132 | try:
133 | python_path = await get_python_path()
134 | subprocess.check_call([python_path, '-m', 'pip', 'install', f'{name}=={version}'])
135 | return web.json_response({'message': 'Replacement successful'})
136 | except subprocess.CalledProcessError as e:
137 | logging.error(f"Error replacing dependency: {name} with version {version}")
138 | return web.json_response({'error': str(e)}, status=500)
139 |
140 | comfyui_versions_cache = None
141 |
142 | async def get_comfyui_versions(request):
143 | global comfyui_versions_cache
144 | if comfyui_versions_cache is not None:
145 | return web.json_response(comfyui_versions_cache)
146 |
147 | try:
148 | async with aiohttp.ClientSession() as session:
149 | async with session.get("https://api.github.com/repos/comfyanonymous/ComfyUI/commits?per_page=1000") as response:
150 | if response.status == 200:
151 | commits = await response.json()
152 | versions = [{'id': commit['sha'][:7], 'message': commit['commit']['message'], 'date': commit['commit']['committer']['date']} for commit in commits]
153 | comfyui_versions_cache = versions
154 | return web.json_response(versions)
155 | else:
156 | error_message = f"Error fetching ComfyUI versions: {response.status}"
157 | logging.error(error_message)
158 | raise Exception(error_message)
159 | except Exception as e:
160 | error_message = f"An unexpected error occurred while fetching ComfyUI versions: {str(e)}"
161 | logging.error(error_message)
162 | return web.json_response({'error': error_message}, status=500)
163 |
164 | current_comfyui_version_cache = None
165 |
166 | async def select_comfyui_version(request):
167 | version_id = request.query.get('version_id')
168 | if not version_id:
169 | return web.json_response({'error': 'Version ID is required'}, status=400)
170 | try:
171 | comfyui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
172 | repo = git.Repo(comfyui_path)
173 |
174 | repo.remotes.origin.fetch()
175 |
176 | default_branch = repo.active_branch.name
177 | if default_branch.startswith('version-'):
178 | default_branch = next((b.name for b in repo.branches if not b.name.startswith('version-')), 'main')
179 |
180 | repo.git.checkout(version_id)
181 |
182 | if repo.head.is_detached:
183 | repo.heads[default_branch].checkout()
184 | repo.git.reset('--hard', version_id)
185 |
186 | return web.json_response({'message': f'ComfyUI version switched to {version_id}', 'branch': default_branch})
187 | except Exception as e:
188 | error_message = f"Error selecting ComfyUI version: {str(e)}"
189 | logging.error(error_message)
190 | return web.json_response({'error': error_message}, status=500)
191 |
192 |
193 | async def get_current_comfyui_version(request):
194 | global current_comfyui_version_cache
195 | if current_comfyui_version_cache is not None:
196 | return web.Response(text=current_comfyui_version_cache)
197 | try:
198 | comfyui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
199 | repo = git.Repo(comfyui_path)
200 | current_version = repo.head.object.hexsha[:7]
201 | current_comfyui_version_cache = current_version
202 | return web.Response(text=current_version)
203 | except Exception as e:
204 | error_message = f"Error getting current ComfyUI version: {str(e)}"
205 | logging.error(error_message)
206 | return web.json_response({'error': error_message}, status=500)
207 |
208 |
209 | async def get_current_comfyui_branch(request):
210 | try:
211 | comfyui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
212 | repo = git.Repo(comfyui_path)
213 |
214 | if repo.head.is_detached:
215 | current_branch = 'Detached'
216 | else:
217 | current_branch = repo.active_branch.name
218 |
219 | return web.Response(text=current_branch)
220 | except Exception as e:
221 | error_message = f"Error getting current ComfyUI branch: {str(e)}"
222 | logging.error(error_message)
223 | return web.json_response({'error': error_message}, status=500)
224 |
225 | async def fix_comfyui_detached_branch(request):
226 | try:
227 | comfyui_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
228 | repo = git.Repo(comfyui_path)
229 |
230 | if repo.head.is_detached:
231 | try:
232 | repo.git.checkout('master')
233 | except git.GitCommandError:
234 | try:
235 | repo.git.checkout('main')
236 | except git.GitCommandError as e:
237 | error_message = f"Error switching to default branch for ComfyUI. Git command error: {str(e)}"
238 | logging.error(error_message)
239 | return web.json_response({'error': error_message}, status=500)
240 |
241 | return web.json_response({'message': 'Fixed ComfyUI detached branch'})
242 | except Exception as e:
243 | error_message = f"Error fixing ComfyUI detached branch: {str(e)}"
244 | logging.error(error_message)
245 | return web.json_response({'error': error_message}, status=500)
246 |
247 |
248 | async def get_plugins(request):
249 | try:
250 | plugins_dir = await get_plugins_path()
251 | plugins = []
252 | exclude_files = ["__pycache__", "example_node.py.example", "websocket_image_save.py"]
253 | for entry in os.listdir(plugins_dir):
254 | if entry in exclude_files:
255 | continue
256 | plugin_path = await get_plugin_path(entry)
257 | if os.path.isdir(plugin_path):
258 | plugin_name = entry
259 | if plugin_name.endswith(".disabled"):
260 | plugin_name = plugin_name[:-9]
261 | enabled = False
262 | else:
263 | enabled = True
264 | git_config_path = os.path.join(plugin_path, ".git", "config")
265 | if os.path.exists(git_config_path):
266 | with open(git_config_path, "r") as git_config_file:
267 | git_config = git_config_file.read()
268 | url_match = re.search(r'url = (.*)', git_config)
269 | if url_match:
270 | url = url_match.group(1)
271 | else:
272 | url = ''
273 | else:
274 | url = ''
275 |
276 | try:
277 | repo = git.Repo(plugin_path)
278 |
279 | if repo.head.is_detached:
280 | branch = 'Detached'
281 | else:
282 | branch = repo.active_branch.name
283 |
284 | except git.InvalidGitRepositoryError:
285 | branch = 'unknown'
286 |
287 | plugin = {
288 | 'name': plugin_name,
289 | 'type': 'directory',
290 | 'url': url,
291 | 'version': '',
292 | 'date': '',
293 | 'enabled': enabled,
294 | 'branch': branch
295 | }
296 | plugins.append(plugin)
297 | elif os.path.isfile(plugin_path) and entry.endswith(".py"):
298 | plugin_name = os.path.splitext(entry)[0]
299 | if plugin_name.endswith(".disabled"):
300 | plugin_name = plugin_name[:-9]
301 | enabled = False
302 | else:
303 | enabled = True
304 | plugin = {
305 | 'name': plugin_name,
306 | 'type': 'file',
307 | 'url': '',
308 | 'version': '',
309 | 'date': '',
310 | 'enabled': enabled,
311 | 'branch': ''
312 | }
313 | plugins.append(plugin)
314 | return web.json_response(plugins)
315 | except Exception as e:
316 | error_message = f"An unexpected error occurred while fetching plugins: {str(e)}"
317 | logging.error(error_message)
318 | logging.error(traceback.format_exc())
319 | return web.json_response({'error': error_message}, status=500)
320 |
321 | async def install_plugin(request):
322 | git_url = request.query.get('git_url')
323 | if not git_url:
324 | return web.json_response({'error': 'Git URL is required'}, status=400)
325 | try:
326 | plugin_name = request.query.get('plugin_name')
327 | overwrite = request.query.get('overwrite') == 'true'
328 | logging.info(f"Installing plugin: {plugin_name} from {git_url}")
329 |
330 | plugins_dir = await get_plugins_path()
331 | plugin_path = await get_plugin_path(plugin_name)
332 |
333 | if os.path.exists(plugin_path):
334 | if overwrite:
335 | logging.info(f"Plugin {plugin_name} already exists, overwriting")
336 | shutil.rmtree(plugin_path)
337 | else:
338 | logging.info(f"Plugin {plugin_name} already exists, skipping installation")
339 | return web.json_response({'error': f'Plugin {plugin_name} already exists'}, status=400)
340 |
341 | start_time = time.time()
342 | process = subprocess.Popen(['git', 'clone', git_url, plugin_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
343 |
344 | output = []
345 | while True:
346 | line = process.stdout.readline()
347 | if line:
348 | logging.info(line.strip())
349 | output.append(line)
350 | else:
351 | break
352 |
353 | return_code = process.wait()
354 | end_time = time.time()
355 | logging.info(f"Plugin update took {end_time - start_time:.2f} seconds")
356 |
357 | return web.json_response({'message': 'Plugin updated successfully'})
358 | except git.GitCommandError as e:
359 | error_message = f"Error updating plugin: {plugin_name}. Git command error: {str(e)}"
360 | logging.error(error_message)
361 | return web.json_response({'error': error_message}, status=500)
362 |
363 | except Exception as e:
364 | error_message = f"Error updating plugin: {plugin_name}. {str(e)}"
365 | logging.error(error_message)
366 | return web.json_response({'error': str(e)}, status=500)
367 |
368 | async def select_plugin_version(request):
369 | plugin_name = request.query.get('plugin_name')
370 | version = request.query.get('version')
371 | if not plugin_name or not version:
372 | return web.json_response({'success': False, 'message': 'Plugin name and version are required'}, status=400)
373 | try:
374 | plugin_path = await get_plugin_path(plugin_name)
375 | if not os.path.exists(plugin_path):
376 | return web.json_response({'success': False, 'message': f'Plugin {plugin_name} does not exist'}, status=400)
377 |
378 | repo = git.Repo(plugin_path)
379 |
380 | repo.git.stash()
381 |
382 | try:
383 | repo.git.checkout(version)
384 |
385 | if repo.head.is_detached:
386 | current_branch = 'Detached'
387 | else:
388 | current_branch = repo.active_branch.name
389 |
390 | if current_branch == 'Detached':
391 | temp_branch_name = f'temp-{version[:7]}'
392 | repo.git.checkout('-b', temp_branch_name)
393 | current_branch = temp_branch_name
394 |
395 | plugin = next((p for p in await get_plugins_list() if p['name'] == plugin_name), None)
396 | if plugin:
397 | plugin['version'] = version
398 | plugin['branch'] = current_branch
399 |
400 | return web.json_response({
401 | 'success': True,
402 | 'message': f'Successfully switched plugin {plugin_name} to version {version}',
403 | 'version': version,
404 | 'branch': current_branch
405 | })
406 |
407 | except git.GitCommandError as e:
408 | repo.git.stash('pop')
409 | return web.json_response({'success': False, 'message': f'Git command error: {str(e)}'}, status=500)
410 |
411 | except Exception as e:
412 | logging.error(f"Error selecting plugin version: {str(e)}")
413 | return web.json_response({'success': False, 'message': str(e)}, status=500)
414 |
415 |
416 | async def get_plugins_list():
417 | response = await get_plugins(None)
418 | return json.loads(response.text)
419 |
420 | async def update_plugin(request):
421 | plugin_name = get_query_param(request, 'plugin_name')
422 | if not plugin_name:
423 | return web.json_response({'error': 'Plugin name is required'}, status=400)
424 | try:
425 | plugin_path = await get_plugin_path(plugin_name)
426 | if not os.path.exists(plugin_path):
427 | error_message = f'Plugin {plugin_name} does not exist'
428 | logging.error(error_message)
429 | return web.json_response({'error': error_message}, status=400)
430 | repo = git.Repo(plugin_path)
431 |
432 | try:
433 | default_branch = await get_plugin_default_branch(plugin_name)
434 |
435 | if default_branch and default_branch != 'Detached':
436 | start_time = time.time()
437 | repo.remotes.origin.fetch()
438 | repo.git.reset('--hard', f'origin/{default_branch}')
439 | end_time = time.time()
440 | logging.info(f"Plugin update took {end_time - start_time:.2f} seconds")
441 | else:
442 | start_time = time.time()
443 | repo.remotes.origin.fetch()
444 | repo.git.pull()
445 | end_time = time.time()
446 | logging.info(f"Plugin update took {end_time - start_time:.2f} seconds")
447 |
448 | return web.json_response({'message': 'Plugin updated successfully'})
449 |
450 | except git.GitCommandError as e:
451 | error_message = f"Error updating plugin: {plugin_name}. Git command error: {str(e)}"
452 | logging.error(error_message)
453 | return web.json_response({'error': error_message}, status=500)
454 |
455 | except Exception as e:
456 | error_message = f"Error updating plugin: {plugin_name}. {str(e)}"
457 | logging.error(error_message)
458 | return web.json_response({'error': str(e)}, status=500)
459 |
460 |
461 | async def get_plugin_versions(request):
462 | plugin_name = get_query_param(request, 'plugin_name')
463 | if not plugin_name:
464 | return web.json_response({'error': 'Plugin name is required'}, status=400)
465 | try:
466 | plugin_path = await get_plugin_path(plugin_name)
467 | if not os.path.exists(plugin_path):
468 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
469 | repo = git.Repo(plugin_path)
470 | commits = list(repo.iter_commits())
471 | versions = [{"id": c.hexsha[:7], "message": c.message.strip(), "date": c.committed_datetime.strftime("%Y-%m-%d %H:%M:%S")} for c in commits]
472 |
473 | plugin_author = 'unknown'
474 | try:
475 | remote_urls = list(repo.remote().urls)
476 | if remote_urls:
477 | plugin_author = remote_urls[0].split('/')[-2]
478 | except (AttributeError, IndexError):
479 | pass
480 |
481 | return web.json_response({"versions": versions, "author": plugin_author})
482 | except Exception as e:
483 | logging.error(f"Error in get_plugin_versions: {str(e)}")
484 | return web.json_response({'error': str(e)}, status=500)
485 |
486 | async def view_plugin_requirements(request):
487 | plugin_name = request.query.get('plugin_name')
488 | if not plugin_name:
489 | return web.json_response({'error': 'Plugin name is required'}, status=400)
490 | try:
491 | plugin_path = await get_plugin_path(plugin_name)
492 |
493 | if not os.path.exists(plugin_path):
494 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
495 | requirements_path = os.path.join(plugin_path, 'requirements.txt')
496 | if not os.path.exists(requirements_path):
497 | return web.json_response({'message': 'No requirements found'})
498 | with open(requirements_path, 'r') as file:
499 | requirements = file.read()
500 | return web.json_response(requirements)
501 | except Exception as e:
502 | logging.error(f"Error viewing plugin requirements: {str(e)}")
503 | return web.json_response({'error': str(e)}, status=500)
504 |
505 | async def edit_plugin_requirements(request):
506 | plugin_name = request.query.get('plugin_name')
507 | if not plugin_name:
508 | return web.json_response({'error': 'Plugin name is required'}, status=400)
509 | try:
510 | plugin_path = await get_plugin_path(plugin_name)
511 |
512 | if not os.path.exists(plugin_path):
513 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
514 |
515 | requirements_path = os.path.join(plugin_path, 'requirements.txt')
516 |
517 | requirements = await request.text()
518 | with open(requirements_path, 'w') as file:
519 | file.write(requirements)
520 | return web.json_response({'message': 'Requirements updated successfully'})
521 | except Exception as e:
522 | logging.error(f"Error editing plugin requirements: {str(e)}")
523 | return web.json_response({'error': str(e)}, status=500)
524 |
525 | async def toggle_plugin(request):
526 | plugin_name = request.query.get('plugin_name')
527 | enabled = request.query.get('enabled')
528 | if not plugin_name or enabled is None:
529 | return web.json_response({'error': 'Plugin name and enabled state are required'}, status=400)
530 | try:
531 | plugins_dir = await get_plugins_path()
532 | plugin_path = await get_plugin_path(plugin_name)
533 | disabled_plugin_path = plugin_path + '.disabled'
534 | if not os.path.exists(plugin_path) and not os.path.exists(disabled_plugin_path):
535 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
536 |
537 | if enabled == 'true':
538 | if os.path.exists(disabled_plugin_path):
539 | os.rename(disabled_plugin_path, plugin_path)
540 | else:
541 | if os.path.exists(plugin_path):
542 | os.rename(plugin_path, disabled_plugin_path)
543 |
544 | return web.json_response({'message': f'Plugin {plugin_name} {"enabled" if enabled == "true" else "disabled"} successfully'})
545 | except Exception as e:
546 | logging.error(f"Error toggling plugin: {plugin_name}. {str(e)}")
547 | return web.json_response({'error': str(e)}, status=500)
548 |
549 |
550 | async def open_plugin_folder(request):
551 | plugin_name = request.query.get('plugin_name')
552 | if not plugin_name:
553 | return web.json_response({'error': 'Plugin name is required'}, status=400)
554 | try:
555 | plugin_path = await get_plugin_path(plugin_name)
556 |
557 | if not os.path.exists(plugin_path):
558 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
559 |
560 |
561 | if sys.platform == "win32":
562 | os.startfile(plugin_path)
563 | else:
564 | subprocess.Popen(["xdg-open", plugin_path])
565 |
566 | return web.json_response({'message': 'Plugin folder opened successfully'})
567 | except Exception as e:
568 | logging.error(f"Error opening plugin folder: {plugin_name}. {str(e)}")
569 | return web.json_response({'error': str(e)}, status=500)
570 |
571 | async def open_site_packages_folder(request):
572 | try:
573 | site_packages_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "python_miniconda", "Lib", "site-packages"))
574 |
575 | if sys.platform == "win32":
576 | os.startfile(site_packages_path)
577 | else:
578 | subprocess.Popen(["xdg-open", site_packages_path])
579 |
580 | return web.json_response({'message': 'Site-packages folder opened successfully'})
581 | except Exception as e:
582 | logging.error(f"Error opening site-packages folder: {str(e)}")
583 | return web.json_response({'error': str(e)}, status=500)
584 |
585 | async def check_dependency_conflicts(request):
586 | try:
587 | python_path = await get_python_path()
588 | logging.info("Checking dependency conflicts...")
589 | process = subprocess.Popen([python_path, '-m', 'pip', 'check'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
590 | stdout, stderr = process.communicate()
591 | if stdout:
592 | return web.Response(text=stdout.decode('utf-8', errors='replace'))
593 | elif stderr:
594 | logging.error("Error checking dependency conflicts:")
595 | return web.Response(text=stderr.decode('utf-8', errors='replace'))
596 | else:
597 | logging.info("No conflicts found.")
598 | return web.Response(text="No conflicts found.")
599 | except Exception as e:
600 | logging.error(f"Error checking dependency conflicts: {str(e)}")
601 | return web.json_response({'error': str(e)}, status=500)
602 |
603 |
604 | async def get_dependency_versions(request):
605 | name = request.query.get('name')
606 | if not name:
607 | return web.json_response({'error': 'Name is required'}, status=400)
608 |
609 | if name in version_cache:
610 | return web.json_response(version_cache[name])
611 |
612 | try:
613 | python_path = await get_python_path()
614 | process = subprocess.Popen([python_path, '-m', 'pip', 'install', f'{name}=='], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
615 | _, stderr = process.communicate()
616 | output_text = stderr.decode('utf-8').strip()
617 |
618 | if '(from versions:' in output_text:
619 | start_index = output_text.index('(from versions:') + len('(from versions:')
620 | end_index = output_text.index(')', start_index)
621 | versions_text = output_text[start_index:end_index].strip()
622 | versions = [v.strip() for v in versions_text.split(',')]
623 | formatted_versions = [f"{name}=={version}" for version in versions]
624 |
625 | version_cache[name] = formatted_versions
626 |
627 | return web.json_response(formatted_versions)
628 | else:
629 | return web.json_response([])
630 | except Exception as e:
631 | logging.error(f"Error getting dependency versions for {name}: {str(e)}")
632 | return web.json_response([])
633 |
634 | version_cache = {}
635 |
636 | async def install_dependency_version(request):
637 | name = request.query.get('name')
638 | version = request.query.get('version')
639 | if not name or not version:
640 | return web.json_response({'error': 'Name and version are required'}, status=400)
641 | try:
642 | python_path = await get_python_path()
643 | process = subprocess.Popen([python_path, '-m', 'pip', 'install', f'{name}=={version}'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
644 |
645 | output = []
646 | while True:
647 | line = process.stdout.readline()
648 | if line:
649 | logging.info(line.strip())
650 | output.append(line)
651 | else:
652 | break
653 |
654 | if process.returncode == 0:
655 | filtered_output = [line for line in output if not line.startswith('WARNING')]
656 | response_data = {'message': 'Installation successful', 'output': ''.join(filtered_output), 'error': ''}
657 | logging.info(f"Message: {response_data}")
658 | return web.json_response(response_data)
659 | else:
660 | response_data = {'message': 'Installation failed', 'output': ''.join(output), 'error': ''}
661 | logging.error(f"Message: {response_data}")
662 | return web.json_response(response_data, status=500)
663 | except subprocess.CalledProcessError as e:
664 | error_message = f"Error installing dependency: {name}=={version}. {str(e)}"
665 | logging.error(error_message)
666 | logging.error(e.output.decode('utf-8'))
667 | return web.json_response({'error': error_message}, status=500)
668 |
669 |
670 | async def install_plugin_requirements(request):
671 | plugin_name = request.query.get('plugin_name')
672 | if not plugin_name:
673 | return web.json_response({'error': 'Plugin name is required'}, status=400)
674 |
675 | try:
676 | plugin_path = await get_plugin_path(plugin_name)
677 | requirements_path = os.path.join(plugin_path, 'requirements.txt')
678 |
679 | if not os.path.exists(plugin_path):
680 | error_message = f'Plugin {plugin_name} does not exist'
681 | logging.error(error_message)
682 | return web.json_response({'error': error_message}, status=400)
683 |
684 | if not os.path.exists(requirements_path):
685 | logging.info(f'No requirements found for plugin {plugin_name}')
686 | return web.json_response({'message': 'No requirements found for this plugin'})
687 |
688 | python_path = await get_python_path()
689 |
690 | logging.info(f"Installing requirements for plugin: {plugin_name}")
691 |
692 | process = await asyncio.create_subprocess_exec(
693 | python_path, '-m', 'pip', 'install', '-r', requirements_path,
694 | stdout=asyncio.subprocess.PIPE,
695 | stderr=asyncio.subprocess.STDOUT
696 | )
697 |
698 | response = web.StreamResponse()
699 | response.headers['Content-Type'] = 'text/plain'
700 | await response.prepare(request)
701 |
702 | while True:
703 | line = await process.stdout.readline()
704 | if not line:
705 | break
706 | await response.write(line)
707 | await response.drain()
708 |
709 | await process.wait()
710 |
711 | if process.returncode == 0:
712 | await response.write(b"Installation completed successfully.\n")
713 | else:
714 | await response.write(b"Installation failed.\n")
715 |
716 | await response.write_eof()
717 | return response
718 |
719 | except Exception as e:
720 | error_message = f"Error installing requirements for plugin: {plugin_name}. {str(e)}"
721 | logging.error(error_message)
722 | return web.json_response({'error': error_message}, status=500)
723 |
724 |
725 | async def checkout_plugin_branch(request):
726 | plugin_name = get_query_param(request, 'plugin_name')
727 | if not plugin_name:
728 | return web.json_response({'error': 'Plugin name is required'}, status=400)
729 | try:
730 | plugin_path = await get_plugin_path(plugin_name)
731 | if not os.path.exists(plugin_path):
732 | error_message = f'Plugin {plugin_name} does not exist'
733 | logging.error(error_message)
734 | return web.json_response({'error': error_message}, status=400)
735 | repo = git.Repo(plugin_path)
736 |
737 | if repo.head.is_detached:
738 | try:
739 | repo.git.checkout('master')
740 | except git.GitCommandError:
741 | try:
742 | repo.git.checkout('main')
743 | except git.GitCommandError as e:
744 | error_message = f"Error switching to default branch for plugin: {plugin_name}. Git command error: {str(e)}"
745 | logging.error(error_message)
746 | return web.json_response({'error': error_message}, status=500)
747 | else:
748 | default_branch = repo.active_branch.name
749 | try:
750 | repo.git.checkout(default_branch)
751 | except git.GitCommandError as e:
752 | error_message = f"Error switching to default branch for plugin: {plugin_name}. Git command error: {str(e)}"
753 | logging.error(error_message)
754 | return web.json_response({'error': error_message}, status=500)
755 |
756 | return web.json_response({'message': f'Switched to default branch for plugin: {plugin_name}'})
757 | except Exception as e:
758 | error_message = f"Error switching to default branch for plugin: {plugin_name}. {str(e)}"
759 | logging.error(error_message)
760 | return web.json_response({'error': error_message}, status=500)
761 |
762 | def get_query_param(request, param_name):
763 | if isinstance(request, aiohttp.web.Request):
764 | return request.rel_url.query.get(param_name)
765 | else:
766 | query_params = urllib.parse.parse_qs(request)
767 | param_values = query_params.get(param_name, [])
768 | return param_values[0] if param_values else None
769 |
770 | async def get_plugin_default_branch(request):
771 | plugin_name = get_query_param(request, 'plugin_name')
772 | if not plugin_name:
773 | return web.json_response({'error': 'Plugin name is required'}, status=400)
774 | try:
775 | plugin_path = await get_plugin_path(plugin_name)
776 | if not os.path.exists(plugin_path):
777 | error_message = f'Plugin {plugin_name} does not exist'
778 | logging.error(error_message)
779 | raise Exception(error_message)
780 | repo = git.Repo(plugin_path)
781 |
782 | if repo.head.is_detached:
783 | default_branch = 'Detached'
784 | else:
785 | default_branch = repo.active_branch.name
786 |
787 | return web.json_response({'default_branch': default_branch})
788 | except Exception as e:
789 | error_message = f"Error getting default branch for plugin: {plugin_name}. {str(e)}"
790 | return web.json_response({'error': error_message}, status=500)
791 |
792 | async def fix_plugin_branch(request):
793 | plugin_name = request.query.get('plugin_name')
794 | if not plugin_name:
795 | return web.json_response({'error': 'Plugin name is required'}, status=400)
796 | try:
797 | plugin_path = await get_plugin_path(plugin_name)
798 | repo = git.Repo(plugin_path)
799 |
800 | for branch in ['main', 'master']:
801 | try:
802 | repo.git.checkout(branch)
803 | return web.json_response({'message': f'Switched to {branch} branch for plugin: {plugin_name}'})
804 | except git.GitCommandError:
805 | continue
806 |
807 | default_branch = repo.active_branch.name
808 | repo.git.checkout(default_branch)
809 | return web.json_response({'message': f'Switched to default branch {default_branch} for plugin: {plugin_name}'})
810 | except Exception as e:
811 | error_message = f"Error fixing branch for plugin: {plugin_name}. {str(e)}"
812 | logging.error(error_message)
813 | return web.json_response({'error': error_message}, status=500)
814 |
815 |
816 | async def get_plugin_details(request):
817 | plugin_name = request.query.get('plugin_name')
818 | if not plugin_name:
819 | return web.json_response({'error': 'Plugin name is required'}, status=400)
820 | try:
821 | plugin_path = await get_plugin_path(plugin_name)
822 | if not os.path.exists(plugin_path):
823 | return web.json_response({'error': f'Plugin {plugin_name} does not exist'}, status=400)
824 |
825 | plugin_details = {
826 | 'name': plugin_name,
827 | 'type': 'directory' if os.path.isdir(plugin_path) else 'file',
828 | 'url': '',
829 | 'version': '',
830 | 'date': '',
831 | 'enabled': not plugin_name.endswith(".disabled"),
832 | 'branch': ''
833 | }
834 |
835 | git_config_path = os.path.join(plugin_path, ".git", "config")
836 | if os.path.exists(git_config_path):
837 | with open(git_config_path, "r") as git_config_file:
838 | git_config = git_config_file.read()
839 | url_match = re.search(r'url = (.*)', git_config)
840 | if url_match:
841 | plugin_details['url'] = url_match.group(1)
842 |
843 | try:
844 | repo = git.Repo(plugin_path)
845 |
846 | if repo.head.is_detached:
847 | plugin_details['branch'] = 'Detached'
848 | else:
849 | plugin_details['branch'] = repo.active_branch.name
850 |
851 | except git.InvalidGitRepositoryError:
852 | plugin_details['branch'] = 'unknown'
853 |
854 | return web.json_response(plugin_details)
855 | except Exception as e:
856 | logging.error(f"Error getting details for plugin {plugin_name}: {str(e)}")
857 | return web.json_response({'error': str(e)}, status=500)
858 |
859 |
860 | def get_package_version(module_path):
861 | try:
862 | parts = module_path.split('.')
863 | package_name = parts[0]
864 |
865 | url = f"https://pypi.org/pypi/{package_name}/json"
866 | response = requests.get(url)
867 | data = response.json()
868 |
869 | versions = list(data["releases"].keys())
870 | latest_version = versions[-1]
871 |
872 | return f"{package_name}=={latest_version}"
873 | except requests.exceptions.RequestException as e:
874 | logging.error(f"Error fetching package metadata for {package_name}: {str(e)}")
875 | return f"Error: Failed to fetch package metadata for {package_name}"
876 | except KeyError:
877 | logging.error(f"Package {package_name} not found on PyPI")
878 | return f"Error: Package {package_name} not found on PyPI"
879 | except Exception as e:
880 | logging.error(f"Error getting package version for {module_path}: {str(e)}")
881 | return f"Error: {str(e)}"
882 |
883 | async def get_module_version(request):
884 | module_path = request.query.get('module_path')
885 | if not module_path:
886 | return web.json_response({'error': 'Module path is required'}, status=400)
887 | try:
888 | result = get_package_version(module_path)
889 | return web.Response(text=result)
890 | except Exception as e:
891 | logging.error(f"Error getting module version for {module_path}: {str(e)}")
892 | return web.json_response({'error': str(e)}, status=500)
893 |
894 |
895 | async def get_mjstyle_json(request):
896 | name = request.match_info['name']
897 | base_path = os.path.dirname(os.path.abspath(__file__))
898 | file_path = os.path.join(base_path, 'docs', 'mjstyle', f'{name}.json')
899 |
900 | if os.path.exists(file_path):
901 | with open(file_path, 'rb') as f:
902 | json_data = f.read()
903 | return web.Response(body=json_data, content_type='application/json')
904 | else:
905 | return web.Response(status=404)
906 |
907 | async def get_marked_js(request):
908 | base_path = os.path.dirname(os.path.abspath(__file__))
909 | file_path = os.path.join(base_path, 'web', 'lib', 'marked.min.js')
910 |
911 | if os.path.exists(file_path):
912 | with open(file_path, 'rb') as f:
913 | js_data = f.read()
914 | return web.Response(body=js_data, content_type='application/javascript')
915 | else:
916 | return web.Response(status=404)
917 |
918 | async def get_purify_js(request):
919 | base_path = os.path.dirname(os.path.abspath(__file__))
920 | file_path = os.path.join(base_path, 'web', 'lib', 'purify.min.js')
921 |
922 | if os.path.exists(file_path):
923 | with open(file_path, 'rb') as f:
924 | js_data = f.read()
925 | return web.Response(body=js_data, content_type='application/javascript')
926 | else:
927 | return web.Response(status=404)
928 |
929 |
930 | def load_api_config():
931 | try:
932 | current_dir = os.path.dirname(os.path.realpath(__file__))
933 | config_path = os.path.join(current_dir, 'Comflyapi.json')
934 |
935 | if not os.path.exists(config_path):
936 | return {}
937 |
938 | with open(config_path, 'r') as f:
939 | config = json.load(f)
940 | return config
941 | except Exception as e:
942 | logging.error(f"Error loading API config: {str(e)}")
943 | return {}
944 |
945 | async def get_config(request):
946 | config = load_api_config()
947 | return web.json_response(config)
948 |
949 |
950 | app = web.Application()
951 |
952 | # Configure CORS
953 | cors = setup(app, defaults={
954 | "*": ResourceOptions(
955 | allow_credentials=True,
956 | expose_headers="*",
957 | allow_headers="*",
958 | allow_methods="*",
959 | )
960 | })
961 |
962 |
963 | # Define routes
964 | routes = [
965 | ("/fix_plugin_branch", fix_plugin_branch),
966 | ("/api/get_config", get_config),
967 | ("/lib/marked.min.js", get_marked_js),
968 | ("/lib/purify.min.js", get_purify_js),
969 | ("/mjstyle/{name}.json", get_mjstyle_json),
970 | ("/get_dependencies", get_dependencies),
971 | ("/install_dependency", install_dependency),
972 | ("/manage_dependency", manage_dependency),
973 | ("/replace_dependency", replace_dependency),
974 | ("/get_comfyui_versions", get_comfyui_versions),
975 | ("/select_comfyui_version", select_comfyui_version),
976 | ("/get_current_comfyui_version", get_current_comfyui_version),
977 | ("/get_current_comfyui_branch", get_current_comfyui_branch),
978 | ("/fix_comfyui_detached_branch", fix_comfyui_detached_branch),
979 | ("/get_plugins", get_plugins),
980 | ("/install_plugin", install_plugin),
981 | ("/select_plugin_version", select_plugin_version),
982 | ("/update_plugin", update_plugin),
983 | ("/get_plugin_details", get_plugin_details),
984 | ("/get_plugin_versions", get_plugin_versions),
985 | ("/view_plugin_requirements", view_plugin_requirements),
986 | ("/edit_plugin_requirements", edit_plugin_requirements),
987 | ("/open_plugin_folder", open_plugin_folder),
988 | ("/open_site_packages_folder", open_site_packages_folder),
989 | ("/toggle_plugin", toggle_plugin),
990 | ("/check_dependency_conflicts", check_dependency_conflicts),
991 | ("/get_dependency_versions", get_dependency_versions),
992 | ("/install_dependency_version", install_dependency_version),
993 | ("/install_plugin_requirements", install_plugin_requirements),
994 | ("/get_plugin_default_branch", get_plugin_default_branch),
995 | ("/checkout_plugin_branch", checkout_plugin_branch),
996 | ("/get_module_version", get_module_version),
997 | ]
998 |
999 | # Add routes to the application with CORS
1000 | for route in routes:
1001 | resource = cors.add(app.router.add_resource(route[0]))
1002 | cors.add(resource.add_route("GET", route[1]))
1003 | cors.add(resource.add_route("POST", route[1]))
1004 |
1005 | def stop_process_on_port(port):
1006 | for proc in psutil.process_iter(['pid', 'name']):
1007 | try:
1008 | try:
1009 | connections = proc.net_connections()
1010 | except AttributeError:
1011 | connections = proc.connections()
1012 |
1013 | for conn in connections:
1014 | if conn.laddr.port == port:
1015 | proc.terminate()
1016 | proc.wait()
1017 | except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
1018 | pass
1019 |
1020 | def start_api_server():
1021 | stop_process_on_port(8080)
1022 | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
1023 | web.run_app(app, port=8080, access_log=None, print=None)
1024 |
1025 | if __name__ == '__main__':
1026 | print("\033[32m ** Comfly Loaded :\033[33m fly, just fly\033[0m")
1027 | start_api_server()
1028 |
--------------------------------------------------------------------------------
/Comflyapi.json:
--------------------------------------------------------------------------------
1 | {
2 | "api_key": ""
3 | }
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 
3 |
4 |
5 |
6 |
7 | # 👋🏻 Welcome to Comfly
8 |
9 |
10 |
11 |
12 |
13 | 我喜欢comfyui,它就像风一样的自由,所以我取名为:comfly
14 | 同样我也喜欢绘画和设计,所以我非常佩服每一位画家,艺术家,在ai的时代,我希望自己能接收ai知识的同时,也要记住尊重关于每个画师的版权问题。
15 | 我一直认为ai最好的方式应该是在购买版权的前提下,有序的完成ai的良性发展. 在学习comfyui的这段旅程中,我遇到了,几位可爱的小伙伴。
16 |
17 | 并且他们格外的温暖,调皮,傻不拉几。虽然他们没有参与这个项目,但是有几位朋友帮忙做测试。
18 | 第一张图片由"走走走"修改后的版本.
19 |
20 | 
21 |
22 |
23 | > **Warning**
24 | >
25 | > 插件只在windows11上测试,mac电脑后续我也会测试,如果其他系统有任何问题,可以提交issues
26 | > 目前界面没有做太多的美化,有时间再说,必要性不到,主要是功能性插件
27 |
28 | # 更新 Update:
29 |
30 | 20250613:
31 |
32 | `Flux节点`: 新增bfl官方节点:Comfly_Flux_Kontext_bfl节点,价格不变
33 |
34 |
35 | 20250611:
36 |
37 | `Flux节点`: Comfly_Flux_Kontext_Edit节点支持设置出图数量(1-4张范围),这个节点不会消耗上传图片费用,直接传入图片即可,
38 | 跟Comfly_Flux_Kontext一样,就是上传图片不会扣费,图片输入支持base64图片编码格式,可以做为稳定性的备用节点。
39 |
40 | 20250601:
41 |
42 | `Flux节点`: Comfly_Flux_Kontext节点支持设置出图数量(1-4张范围),去掉了match_input_image对输入图片尺寸选项。
43 | 支持多图输入。已经支持对上一次生成的图片再次提示词编辑(但只有当出土数量选择1时才可以使用这个。)
44 |
45 |
46 |
47 | 20250530:
48 |
49 | `Flux节点`: 新增Comfly_Flux_Kontext节点,支持:flux-kontext-pro和flux-kontext-max模型,按次收费:pro模型大约0.096元,max大约0.192元,比官方便宜很多。
50 |
51 |
52 | 20250526:
53 |
54 | `Jimeng即梦视频节点`: 新增ComflyJimengVideoApi节点。即梦视频,按次收费,5s是0.6元,10s是1.2元。
55 |
56 | 查看更新/Update
57 |
58 | 
59 |
60 |
61 |
62 | 20250518:
63 |
64 | `Kling节点`: 可灵节点新增kling-v2-master的可灵2.0模型。价格很贵,按需使用。
65 |
66 | 20250429:
67 |
68 | `Chatgpt节点`: Comfly_gpt_image_1_edit新增chats输出口,输出多轮对话。
69 | 新增clear_chats,当为Ture的时候,只能image输入什么图片修改什么图片,不支持显示上下文对话。
70 | 当为Flase的时候,支持对上一次生成的图片进行二次修改。支持显示上下文对话。并且支持多图模式下新增图片参考。
71 |
72 |
73 | 查看更新/Update
74 |
75 | 
76 |
77 | 
78 |
79 |
80 |
81 | 20250425:
82 |
83 | 视频教程: https://www.bilibili.com/video/BV1jxLUz9ECX
84 |
85 | `Chatgpt节点`:
86 | 新增Comfly_gpt_image_1和Comfly_gpt_image_1_edit官方gpt_image_1模型api接口节点。
87 |
88 | 
89 |
90 | 模型名都是gpt_image_1,区别只是分组不同:
91 |
92 | 一共四个分组:default默认分组为官方逆向,价格便宜,缺点就是不稳定,速度慢。按次收费。不支持额外参数选择。这个分组的apikey只能用于ComflyChatGPTApi节点。
93 |
94 | 其他三个组都是官方api组,最优惠的目前是ssvip组。分组需要再令牌里面去修改选择。这3个官方分组优点就是速度快,稳定性高。支持官方参数调整。
95 | 缺点就是贵,但是也比官方便宜。大家可以按照自己的情况选择。这3个分组的令牌的apikey只能用在下面2个新节点上面!!!
96 |
97 | 1. Comfly_gpt_image_1 节点:文生图,有耕读参数调整,支持调整生图限制为low。
98 |
99 | 2. Comfly_gpt_image_1_edit 节点:图生图,支持mask遮罩,支持多图参考。
100 |
101 |
102 | 查看更新/Update
103 |
104 | 
105 |
106 | 
107 |
108 |
109 |
110 | 20250424:
111 | `Chatgpt节点`: ComflyChatGPTApi节点新增官方gpt-image-1,按次计费 0.06,
112 | 旧版的gpt4o-image,gpt4o-image-vip,sora_image, sora_image-vip可以做为备选。首选gpt-image-1。
113 |
114 | `jimeng即梦节点`: 即梦的ComflyJimengApi节点新增参考图生成图片,image url图片链接参考生成图片。
115 | 注意:参考图生成图片会额外消耗上传图片的token费用(具体根据你图片大小来,大部分都是0.000几到0.00几元不等。图片链接有时效性,不做长期储存),
116 | 这个只适用于你没有image url图片链接的前提下使用。
117 | 如果你有image url图片链接,就直接填写在image url里面既可以。
118 |
119 |
120 | 查看更新/Update
121 |
122 | 
123 |
124 | 
125 |
126 |
127 |
128 | 20250422:
129 | `Chatgpt节点`: ComflyChatGPTApi节点新增chats输出口,输出多轮对话。
130 | 新增clear_chats,当为Ture的时候,只能image输入什么图片修改什么图片,不支持显示上下文对话。
131 | 当为Flase的时候,支持对上一次生成的图片进行二次修改。支持显示上下文对话。
132 |
133 |
134 | 查看更新/Update
135 |
136 | 
137 |
138 | 
139 |
140 | 
141 |
142 |
143 |
144 |
145 | 20250418:
146 | `jimeng即梦节点`: 新增即梦的ComflyJimengApi节点。
147 | 目前只支持文生图,使用的是 https://ai.comfly.chat 的 api key
148 |
149 | 参数说明:
150 | use_pre_llm:开启文本扩写,会针对输入prompt进行扩写优化,如果输入prompt较短建议开启,如果输入prompt较长建议关闭。
151 | scale:影响文本描述的程度,默认值:2.5,取值范围:[1, 10]
152 | width,height:生成图像的宽和高,默认值:1328,取值范围:[512, 2048]
153 | add_logo:是否添加水印。True为添加,False不添加。默认不添加
154 | opacity:水印的不透明度,取值范围0-1,1表示完全不透明,默认0.3
155 |
156 |
157 | 查看更新/Update
158 |
159 | 
160 |
161 | 
162 |
163 |
164 |
165 | `ai模块`: 简单化ai模块,支持上传文件解析(复杂的图片pdf会根据具体模型来确定).优化交互体验。
166 | 设置里面可以设置api key。
167 |
168 |
169 | 查看更新/Update
170 |
171 | 
172 | 
173 | 
174 |
175 |
176 |
177 | 20250401:
178 | `所有节点`: 所有调用apikey的节点新增apikey输入框。优化可灵图生图视频节点。
179 |
180 | 20250329:
181 | `Chatgpt节点`: 新增openai的ComflyChatGPTApi节点,。
182 | 目前单图和多图输入,文本输入,生成图片,图片编辑.使用的是 https://ai.comfly.chat 的 api key
183 | 固定一次生成消耗0.06元(显示是逆向api,稳定性还不高,想尝鲜的可以注册网站用免费送的0.2美金玩玩)
184 | 速度不快,因为官网速度也不快,所以需要点耐心。 files输入接口还没有完善,先忽略。
185 | 用sora_image现在先对稳定点
186 |
187 |
188 | 查看更新/Update
189 |
190 | 
191 |
192 |
193 | 
194 |
195 |
196 |
197 |
198 | 20250325:
199 |
200 | `Kling节点`: 新增可灵Comfly_lip_sync对口型节点,生成效果还行吧。速度也一般般。支持中文和英文。
201 |
202 | `Gemmi节点`: ComflyGeminiAPI节点resolution新增:object_image size,subject_image size,scene_image size根据输入的图片的尺寸来确定输出图片的尺寸。增加image url输出接口。
203 |
204 | `Doubao豆包节点`: ComflySeededit节点文字驱动生成图片,编辑图片。支持添加自己的水印logo。目前只支持单图修改和参考。使用的是 https://ai.comfly.chat 的 api key
205 |
206 | 用于编辑图像的提示词 。建议:
207 |
208 | 添加/删除实体:添加/删除xxx(删除图上的女孩/添加一道彩虹)
209 |
210 | 修改实体:把xxx改成xxx(把手里的鸡腿变成汉堡)
211 |
212 | 修改风格:改成xxx风格(改成漫画风格)
213 |
214 | 修改色彩:把xxx改成xx颜色(把衣服改成粉色的)
215 |
216 | 修改动作:修改表情动作(让他哭/笑/生气)
217 |
218 | 修改环境背景:背景换成xxx,在xxx(背景换成海边/在星空下)
219 |
220 | 1:图片格式:JPG(JPEG), PNG, BMP 等常见格式, 建议使用JPG格式.
221 |
222 | 2:图片要求:小于4.7 MB,小于4096*4096
223 |
224 | 3:长边与短边比例在3以内,超出此比例或比例相对极端,会导致报错
225 |
226 |
227 |
228 | 查看更新/Update
229 |
230 | 
231 |
232 | 
233 |
234 |
235 |
236 |
237 | 20250321:`Gemmi节点`: 谷歌ComflyGeminiAPI节点支持生成文生多图(最多4张,控制时间)。
238 | 支持多图片参考,我是借用google labs的whisk思路,我感觉比较实用,并不需要太多参考图,3种足够.无需谷歌账户和梯子魔法就能用。使用的是 https://ai.comfly.chat 的 api key
239 |
240 | 查看更新/Update
241 |
242 | 
243 |
244 |
245 |
246 | 20250319:`Gemmi节点`: 新增谷歌ComflyGeminiAPI节点,gemini-2.0-flash-exp-image多模态模型。
247 | 目前只支持简单的单图输入和输出,图片回复,图片编辑.
248 |
249 | 查看更新/Update
250 |
251 | 
252 |
253 |
254 |
255 |
256 | 20250318:`kling节点`: 新增可灵文生视频,图生视频,视频延长(只支持v1.0模型)3个节点.
257 | 可灵视频生成时间大概要5-6分钟左右,使用的是 https://ai.comfly.chat 的 api key.
258 |
259 | 查看更新/Update
260 |
261 | 
262 |
263 |
264 |
265 | 20250220:`chat按钮`: 修复在ai模块单击选择模型不生效问题。 删除一些节点,简化mj节点组。
266 |
267 | 20250219:`mj按钮`: 修复在ai模块里面,mj生成图片后点击U1等按钮失效问题。新增:ai模块上方的模型选择可以双击搜索模型功能。
268 | 删除了侧边栏helpinfo按钮。
269 |
270 | 查看更新/Update
271 |
272 | 
273 |
274 |
275 |
276 | 20241021:`comfly按钮`: 修改悬浮按钮关闭方式,直接按键盘的“A”+“X”按钮关闭和隐藏悬浮按钮,再次按键盘快捷键:“A”+“S”按钮显示悬浮按钮
277 | 注意:A不是Alt,而只是键盘的字母键而已!!!!!
278 |
279 | 查看更新/Update
280 |
281 | 
282 |
283 |
284 |
285 |
286 | 20241012:`插件页面`: 增加Re按钮:插件增加查看本地readme按钮。修改一些更新按钮的bug。
287 |
288 | 查看更新/Update
289 |
290 | 
291 |
292 | 
293 |
294 |
295 |
296 |
297 | 20240913:`comfly按钮`: 修改悬浮按钮,鼠标放置上去可以点击右上角关闭按钮,再次按键盘快捷键:“A”+“S”按钮显示悬浮按钮
298 |
299 | 查看更新/Update
300 |
301 | 
302 |
303 |
304 |
305 |
306 |
307 | # 主要功能界面:
308 |
309 | ## Ai Chat and Midjourney
310 |
311 | * `Ai Chatbox `: 这个就是悬浮按钮,可以点击展开功能模块
312 |
313 | 
314 |
315 |
316 |
317 | * `Midjourney `:和各类大语言模型
318 |
319 | 
320 |
321 |
322 |
323 | * `Midjourney `:的节点组,workflow:
324 |
325 |
326 | 
327 |
328 |
329 |
330 | > \[!IMPORTANT]\
331 | > 由于ai和 midjourney api需要api key,请直接在这个网址:https://ai.comfly.chat
332 | >
333 | > api key可以用在任何支持自定义的第三方软件上面,直接在线使用网址:https://ai.comfly.chat/chat
334 | >
335 | > 本插件还带有coze,kling可灵文生图节点,都是免费的,需要自己填写自己的bot api或者cookie即可。
336 | >
337 | > ai模块有suno还有几个大模型没有适配,现在有点忙,还没有时间测试和适配,后续有时间再说。
338 | >
339 |
340 | ## api key数据填写文件:Comflyapi.json
341 |
342 | 
343 |
344 |
345 |
346 | # 🥵 Comfly的QQ群 / my wechat
347 |
348 | 
349 |
350 | 
351 |
352 |
353 |
354 |
355 | ## :sparkling_heart:依赖管理器
356 | * `Dependencies `: 这个主要是用来管理comfyui安装好的依赖,安装和卸载,包括查看依赖的所有版本,还有环境的冲突有哪些
357 |
358 | 
359 |
360 |
361 |
362 |
363 | ## :tangerine:Comfyui版本管理
364 | * `Comfyui version`: comfyui本体版本的管理和切换.
365 |
366 |
367 | 
368 |
369 |
370 |
371 | ## :cactus:插件管理
372 | * `Costum nodes`: 管理插件的安装,启用和禁用,插件的版本管理,更新等等.
373 |
374 | 
375 |
376 |
377 |
378 |
379 | ## :partying_face:插件依赖文件安装和修改
380 | * `Requirements`: 可以查看和修改依赖版本,并且直接一键安装.
381 |
382 |
383 | 
384 |
385 |
386 |
387 |
388 | # :dizzy:插件有参考项目 Comfy Resources:
389 |
390 | https://github.com/kijai/ComfyUI-KJNodes
391 |
392 | 感谢原项目:
393 | https://github.com/comfyanonymous/ComfyUI
394 |
395 |
396 |
397 | ## Star History
398 |
399 | [](https://star-history.com/#ainewsto/Comfyui_Comfly&Date)
400 |
401 |
402 |
403 | ## 🚀 About me
404 | * website: https://comfly.chat
405 | * Welcome valuable suggestions! 📧 **Email**: [3508432500@qq.com](mailto:1544007699@qq.com)
406 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | from .Comfly import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS, WEB_DIRECTORY
2 | from . import AiHelper
3 |
4 | __all__ = ['AiHelper', 'NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY', 'AiHelperExtension']
5 |
6 | def start_ai_helper():
7 | import threading
8 | import subprocess
9 | import os
10 | import sys
11 | import aiohttp
12 |
13 | def run_ai_helper():
14 | ai_helper_path = os.path.join(os.path.dirname(__file__), "AiHelper.py")
15 | subprocess.run([sys.executable, ai_helper_path])
16 |
17 | ai_helper_thread = threading.Thread(target=run_ai_helper)
18 | ai_helper_thread.start()
19 |
20 | start_ai_helper()
21 |
22 |
--------------------------------------------------------------------------------
/docs/mjstyle/mj_art.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Conceptual art",
4 | "prompt": "Conceptual art",
5 | "negative_prompt": "ugly, deformed, noisy, blurry, NSFW"
6 | },
7 | {
8 | "name": "Social realism",
9 | "prompt": "Social realism",
10 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
11 | },
12 | {
13 | "name": "interior architecture",
14 | "prompt": "interior architecture",
15 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
16 | },
17 | {
18 | "name": "modern",
19 | "prompt": "modern",
20 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
21 | },
22 | {
23 | "name": "anime",
24 | "prompt": "anime",
25 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
26 | },
27 | {
28 | "name": "Minimalism",
29 | "prompt": "Minimalism",
30 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
31 | },
32 | {
33 | "name": "Romanticism",
34 | "prompt": "Romanticism",
35 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
36 | },
37 | {
38 | "name": "Gothic art",
39 | "prompt": "Gothic art",
40 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
41 | },
42 | {
43 | "name": "American propaganda poster",
44 | "prompt": "American propaganda poster",
45 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
46 | },
47 | {
48 | "name": "Expressionism",
49 | "prompt": "Expressionism",
50 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
51 | },
52 | {
53 | "name": "Realism",
54 | "prompt": "Realism",
55 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
56 | },
57 | {
58 | "name": "Contemporary art",
59 | "prompt": "Contemporary art",
60 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
61 | },
62 | {
63 | "name": "Dutch Golden Age painting",
64 | "prompt": "Dutch Golden Age painting",
65 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
66 | },
67 | {
68 | "name": "Pop art",
69 | "prompt": "Pop art",
70 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
71 | },
72 | {
73 | "name": "Monet",
74 | "prompt": "Monet",
75 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
76 | },
77 | {
78 | "name": "Northern Renaissance",
79 | "prompt": "Northern Renaissance",
80 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
81 | },
82 | {
83 | "name": "Dadaism",
84 | "prompt": "Dadaism",
85 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
86 | },
87 | {
88 | "name": "Luminism",
89 | "prompt": "Luminism",
90 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
91 | },
92 | {
93 | "name": "Art Deco",
94 | "prompt": "Art Deco",
95 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
96 | },
97 | {
98 | "name": "rococo style",
99 | "prompt": "rococo style",
100 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
101 | },
102 | {
103 | "name": "Tonalism",
104 | "prompt": "Tonalism",
105 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
106 | },
107 | {
108 | "name": "Fauvism",
109 | "prompt": "Fauvism",
110 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
111 | },
112 | {
113 | "name": "Carl Larsson",
114 | "prompt": "Carl Larsson",
115 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
116 | },
117 | {
118 | "name": "Mannerism",
119 | "prompt": "Mannerism",
120 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
121 | },
122 | {
123 | "name": "Action painting",
124 | "prompt": "Action painting",
125 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
126 | },
127 | {
128 | "name": "Cubist Futurism",
129 | "prompt": "Cubist Futurism",
130 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
131 | },
132 | {
133 | "name": "Futurism",
134 | "prompt": "Futurism",
135 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
136 | },
137 | {
138 | "name": "Neoclassicism",
139 | "prompt": "Neoclassicism",
140 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
141 | },
142 | {
143 | "name": "Baroque",
144 | "prompt": "Baroque",
145 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
146 | },
147 | {
148 | "name": "Genre painting",
149 | "prompt": "Genre painting",
150 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
151 | },
152 | {
153 | "name": "Constructivism",
154 | "prompt": "Constructivism",
155 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
156 | },
157 | {
158 | "name": "by Alfons Mucha",
159 | "prompt": "by Alfons Mucha",
160 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
161 | },
162 | {
163 | "name": "blind box toy style",
164 | "prompt": "blind box toy style",
165 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
166 | },
167 | {
168 | "name": "Ukiyo-e",
169 | "prompt": "Ukiyo-e",
170 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
171 | },
172 | {
173 | "name": "high detail",
174 | "prompt": "high detail",
175 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
176 | },
177 | {
178 | "name": "Op art",
179 | "prompt": "Op art",
180 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
181 | },
182 | {
183 | "name": "Color Field painting",
184 | "prompt": "Color Field painting",
185 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
186 | },
187 | {
188 | "name": "Renaissance",
189 | "prompt": "Renaissance",
190 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
191 | },
192 | {
193 | "name": "Surrealism",
194 | "prompt": "Surrealism",
195 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
196 | },
197 | {
198 | "name": "Bauhaus",
199 | "prompt": "Bauhaus",
200 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
201 | },
202 | {
203 | "name": "Abstract expressionism",
204 | "prompt": "Abstract expressionism",
205 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
206 | },
207 | {
208 | "name": "Pre-Raphaelite Brotherhood",
209 | "prompt": "Pre-Raphaelite Brotherhood",
210 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
211 | },
212 | {
213 | "name": "Impressionism",
214 | "prompt": "Impressionism",
215 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
216 | },
217 | {
218 | "name": "Classicism",
219 | "prompt": "Classicism",
220 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
221 | },
222 | {
223 | "name": "Hyperrealism",
224 | "prompt": "Hyperrealism",
225 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
226 | },
227 | {
228 | "name": "Ghibli-like colours",
229 | "prompt": "Ghibli-like colours",
230 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
231 | },
232 | {
233 | "name": "Abstractionism",
234 | "prompt": "Abstractionism",
235 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
236 | },
237 | {
238 | "name": "pre-rephaëlite painting",
239 | "prompt": "pre-rephaëlite painting",
240 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
241 | },
242 | {
243 | "name": "En plein air",
244 | "prompt": "En plein air",
245 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
246 | },
247 | {
248 | "name": "Pointillism",
249 | "prompt": "Pointillism",
250 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
251 | },
252 | {
253 | "name": "Verism",
254 | "prompt": "Verism",
255 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
256 | },
257 | {
258 | "name": "raised fist",
259 | "prompt": "raised fist",
260 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
261 | },
262 | {
263 | "name": "Ashcan School",
264 | "prompt": "Ashcan School",
265 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
266 | },
267 | {
268 | "name": "Pixar",
269 | "prompt": "Pixar",
270 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
271 | },
272 | {
273 | "name": "Cubism",
274 | "prompt": "Cubism",
275 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
276 | }
277 |
278 | ]
279 |
280 |
--------------------------------------------------------------------------------
/docs/mjstyle/mj_hd.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "UHD",
4 | "prompt": "UHD",
5 | "negative_prompt": "ugly, deformed, noisy, blurry, NSFW"
6 | },
7 | {
8 | "name": "anatomically correct",
9 | "prompt": "anatomically correct",
10 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
11 | },
12 | {
13 | "name": "ccurate",
14 | "prompt": "ccurate",
15 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
16 | },
17 | {
18 | "name": "textured skin",
19 | "prompt": "textured skin",
20 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
21 | },
22 | {
23 | "name": "super detail",
24 | "prompt": "super detail",
25 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
26 | },
27 | {
28 | "name": "high details",
29 | "prompt": "high details",
30 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
31 | },
32 | {
33 | "name": "award winning",
34 | "prompt": "award winning",
35 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
36 | },
37 | {
38 | "name": "best quality",
39 | "prompt": "best quality",
40 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
41 | },
42 | {
43 | "name": "high quality",
44 | "prompt": "high quality",
45 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
46 | },
47 | {
48 | "name": "1080P",
49 | "prompt": "1080P",
50 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
51 | },
52 | {
53 | "name": "retina",
54 | "prompt": "retina",
55 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
56 | },
57 | {
58 | "name": "HD",
59 | "prompt": "HD",
60 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
61 | },
62 | {
63 | "name": "16k",
64 | "prompt": "16k",
65 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
66 | },
67 | {
68 | "name": "8k",
69 | "prompt": "8k",
70 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
71 | },
72 | {
73 | "name": "4K",
74 | "prompt": "4K",
75 | "negative_prompt": "ugly, deformed, noisy, blurry, low contrast, realism, photorealistic"
76 | }
77 |
78 | ]
79 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "Comfyui_Comfly"
3 | description = "NODES: Comfly_Mj, Comfly_mjstyle, Comfly_upload, Comfly_Mju, Comfly_Mjv, Comfly_kling_videoPreview"
4 | version = "1.0.0"
5 | license = {file = "LICENSE"}
6 | dependencies = ["aiohttp", "aiohttp-cors", "GitPython", "numpy", "Pillow", "requests", "matrix-client", "transformers", "huggingface-hub", "psutil"]
7 |
8 | [project.urls]
9 | Repository = "https://github.com/ainewsto/Comfyui_Comfly"
10 | # Used by Comfy Registry https://comfyregistry.org
11 |
12 | [tool.comfy]
13 | PublisherId = "ainewsto"
14 | DisplayName = "Comfyui_Comfly"
15 | Icon = ""
16 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp
2 | aiohttp-cors
3 | GitPython
4 | numpy
5 | Pillow
6 | requests
7 | torch
8 | GitPython
9 | matrix-client
10 | transformers
11 | huggingface-hub
12 | psutil
13 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import torch
3 | from PIL import Image
4 | from typing import List, Union
5 |
6 | def pil2tensor(image: Union[Image.Image, List[Image.Image]]) -> torch.Tensor:
7 | """
8 | Convert PIL image(s) to tensor, matching ComfyUI's implementation.
9 |
10 | Args:
11 | image: Single PIL Image or list of PIL Images
12 |
13 | Returns:
14 | torch.Tensor: Image tensor with values normalized to [0, 1]
15 | """
16 | if isinstance(image, list):
17 | if len(image) == 0:
18 | return torch.empty(0)
19 | return torch.cat([pil2tensor(img) for img in image], dim=0)
20 |
21 | # Convert PIL image to RGB if needed
22 | if image.mode == 'RGBA':
23 | image = image.convert('RGB')
24 | elif image.mode != 'RGB':
25 | image = image.convert('RGB')
26 |
27 | # Convert to numpy array and normalize to [0, 1]
28 | img_array = np.array(image).astype(np.float32) / 255.0
29 |
30 | # Return tensor with shape [1, H, W, 3]
31 | return torch.from_numpy(img_array)[None,]
32 |
33 | def tensor2pil(image: torch.Tensor) -> List[Image.Image]:
34 | """
35 | Convert tensor to PIL image(s), matching ComfyUI's implementation.
36 |
37 | Args:
38 | image: Tensor with shape [B, H, W, 3] or [H, W, 3], values in range [0, 1]
39 |
40 | Returns:
41 | List[Image.Image]: List of PIL Images
42 | """
43 | batch_count = image.size(0) if len(image.shape) > 3 else 1
44 | if batch_count > 1:
45 | out = []
46 | for i in range(batch_count):
47 | out.extend(tensor2pil(image[i]))
48 | return out
49 |
50 | # Convert tensor to numpy array, scale to [0, 255], and clip values
51 | numpy_image = np.clip(255.0 * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)
52 |
53 | # Convert numpy array to PIL Image
54 | return [Image.fromarray(numpy_image)]
--------------------------------------------------------------------------------
/web/js/Comfly_kling_videoPreview.js:
--------------------------------------------------------------------------------
1 | import { app } from "../../../scripts/app.js";
2 | import { api } from "../../../scripts/api.js";
3 |
4 | function fitHeight(node) {
5 | node.setSize([node.size[0], node.computeSize([node.size[0], node.size[1]])[1]]);
6 | node?.graph?.setDirtyCanvas(true);
7 | }
8 |
9 | function chainCallback(object, property, callback) {
10 | if (object == undefined) {
11 | console.error("Tried to add callback to non-existant object");
12 | return;
13 | }
14 | if (property in object) {
15 | const callback_orig = object[property];
16 | object[property] = function () {
17 | const r = callback_orig.apply(this, arguments);
18 | callback.apply(this, arguments);
19 | return r;
20 | };
21 | } else {
22 | object[property] = callback;
23 | }
24 | }
25 |
26 | function addPreviewOptions(nodeType) {
27 | chainCallback(nodeType.prototype, "getExtraMenuOptions", function (_, options) {
28 | let optNew = [];
29 | try {
30 | const previewWidget = this.widgets.find((w) => w.name === "videopreview");
31 |
32 | let url = null;
33 | if (previewWidget?.videoEl?.hidden === false && previewWidget.videoEl.src) {
34 | url = previewWidget.videoEl.src;
35 | }
36 | if (url) {
37 | optNew.push(
38 | {
39 | content: "Open preview",
40 | callback: () => {
41 | window.open(url, "_blank");
42 | },
43 | },
44 | {
45 | content: "Save preview",
46 | callback: () => {
47 | const a = document.createElement("a");
48 | a.href = url;
49 | a.setAttribute("download", new URLSearchParams(previewWidget.value.params).get("filename"));
50 | document.body.append(a);
51 | a.click();
52 | requestAnimationFrame(() => a.remove());
53 | },
54 | }
55 | );
56 | }
57 | if (options.length > 0 && options[0] != null && optNew.length > 0) {
58 | optNew.push(null);
59 | }
60 | options.unshift(...optNew);
61 | } catch (error) {
62 | console.log(error);
63 | }
64 | });
65 | }
66 |
67 | function previewVideo(node, file, type) {
68 | const params = {
69 | filename: file,
70 | type: type,
71 | force_size: "256x?"
72 | };
73 |
74 | const videoUrl = api.apiURL("/view?" + new URLSearchParams(params));
75 |
76 | // 🟢 若已有 video preview widget,复用它
77 | const existing = node.widgets?.find(w => w.name === "videopreview");
78 | if (existing && existing.videoEl) {
79 | existing.videoEl.src = videoUrl;
80 | existing.videoEl.hidden = false;
81 | existing.parentEl.hidden = false;
82 | existing.value.params = params;
83 | fitHeight(node);
84 | return;
85 | }
86 |
87 | // 🆕 创建 widget(首次或缺失时)
88 | const element = document.createElement("div");
89 | const previewWidget = node.addDOMWidget("videopreview", "preview", element, {
90 | serialize: false,
91 | hideOnZoom: false,
92 | getValue() {
93 | return element.value;
94 | },
95 | setValue(v) {
96 | element.value = v;
97 | },
98 | });
99 |
100 | previewWidget.computeSize = function (width) {
101 | if (this.aspectRatio && !this.parentEl.hidden) {
102 | let height = (node.size[0] - 20) / this.aspectRatio + 10;
103 | if (!(height > 0)) height = 0;
104 | this.computedHeight = height + 10;
105 | return [width, height];
106 | }
107 | return [width, -4];
108 | };
109 |
110 | previewWidget.value = { hidden: false, paused: false, params };
111 | previewWidget.parentEl = document.createElement("div");
112 | previewWidget.parentEl.className = "video_preview";
113 | previewWidget.parentEl.style.width = "100%";
114 | element.appendChild(previewWidget.parentEl);
115 |
116 | previewWidget.videoEl = document.createElement("video");
117 | previewWidget.videoEl.controls = true;
118 | previewWidget.videoEl.loop = false;
119 | previewWidget.videoEl.muted = false;
120 | previewWidget.videoEl.style.width = "100%";
121 | previewWidget.videoEl.src = videoUrl;
122 |
123 | previewWidget.videoEl.addEventListener("loadedmetadata", () => {
124 | previewWidget.aspectRatio = previewWidget.videoEl.videoWidth / previewWidget.videoEl.videoHeight;
125 | fitHeight(node);
126 | });
127 |
128 | previewWidget.videoEl.addEventListener("error", () => {
129 | previewWidget.parentEl.hidden = true;
130 | fitHeight(node);
131 | });
132 |
133 | previewWidget.videoEl.autoplay = !previewWidget.value.paused && !previewWidget.value.hidden;
134 | previewWidget.videoEl.hidden = false;
135 |
136 | previewWidget.parentEl.hidden = previewWidget.value.hidden;
137 | previewWidget.parentEl.appendChild(previewWidget.videoEl);
138 | }
139 |
140 | app.registerExtension({
141 | name: "Comfly.VideoPreview",
142 | async beforeRegisterNodeDef(nodeType, nodeData, app) {
143 | if (nodeData?.name === "Comfly_kling_videoPreview") {
144 | nodeType.prototype.onExecuted = function (data) {
145 | previewVideo(this, data.video[0], data.video[1]);
146 | };
147 | addPreviewOptions(nodeType);
148 | }
149 | }
150 | });
151 |
--------------------------------------------------------------------------------
/web/js/Comfly_mjstyle.js:
--------------------------------------------------------------------------------
1 | import{app}from"../../../scripts/app.js";import{$el}from"../../../scripts/ui.js";let pb_cache={};async function getStyles(e){if(pb_cache[e])return pb_cache[e];const t=await fetch(`http://localhost:8080/mjstyle/${e}.json`);if(200===t.status){let s=await t.json();return pb_cache[e]=s,s}}function createTagElement(e,t,s){return $el("label.Comfly_style-model-tag.comfy-btn",{dataset:{tag:e.name,name:e.name,negativePrompt:e.negative_prompt},style:{margin:"5px",display:"inline-flex",alignItems:"center",justifyContent:"space-between",fontSize:"16px"},onclick:t=>{t.preventDefault(),t.stopPropagation();let o=t.currentTarget.querySelector("input[type='checkbox']");o.checked=!o.checked,t.currentTarget.classList.toggle("Comfly_style-model-tag--selected",o.checked),s(e,o.checked)}},[$el("span",{textContent:e.name}),$el("input",{type:"checkbox",checked:t,style:{accentColor:"var(--comfy-menu-bg)"}})])}app.registerExtension({name:"Comfly_mjstyle",async beforeRegisterNodeDef(e,t,s){if(["Comfly_mjstyle"].indexOf(t.name)>=0){const t=e.prototype.onNodeCreated;e.prototype.onNodeCreated=function(){const e=t?.apply(this,arguments);this.properties.values=this.properties.values||[],this.properties.currentStyleType="";const s=this.widgets.find((e=>"styles_type"===e.name)),o=$el("div",{style:{display:"none"}},[$el("div",{textContent:"Selected Styles:",style:{marginBottom:"5px",fontWeight:"bold",fontSize:"18px"}}),$el("div.Comfly_selected-tags-list",{style:{marginBottom:"10px",padding:"5px",border:"1px solid var(--border-color)",borderRadius:"5px",maxHeight:"100px",overflowY:"auto"}})]),l=$el("div.Comfly_style-model-tags-list",{style:{height:"200px",overflowY:"auto",backgroundColor:"var(--comfy-menu-bg)",color:"var(--fg-color)",border:"1px solid var(--border-color)",borderRadius:"5px",padding:"5px",display:"none"},onwheel:e=>{e.stopPropagation()}}),i=$el("button.comfy-btn",{textContent:"Clear all",style:{marginTop:"10px",alignSelf:"flex-end",fontSize:"16px",padding:"4px 10px"},onclick:()=>{this.properties.values=[],n(),p()}}),r=$el("div.Comfly_style-preview",{style:{display:"flex",flexDirection:"column",alignItems:"stretch",position:"relative",height:"100%"}},[o,$el("div",{textContent:"Available Styles:",style:{marginTop:"10px",marginBottom:"5px",fontWeight:"bold",fontSize:"18px"}}),l,$el("div",{style:{display:"flex",justifyContent:"flex-end"}},[i])]),n=()=>{const e=o.querySelector(".Comfly_selected-tags-list");e.innerHTML="",this.properties.values.length>0?(o.style.display="block",this.properties.values.forEach((t=>{e.appendChild(createTagElement({name:t,negative_prompt:""},!0,((e,t)=>{t||(this.properties.values=this.properties.values.filter((t=>t!==e.name)),n(),p())})))}))):o.style.display="none"},p=()=>{pb_cache[this.properties.currentStyleType]&&(l.innerHTML="",pb_cache[this.properties.currentStyleType].forEach((e=>{const t=this.properties.values.includes(e.name);l.appendChild(createTagElement(e,t,((e,t)=>{t&&!this.properties.values.includes(e.name)?this.properties.values.push(e.name):t||(this.properties.values=this.properties.values.filter((t=>t!==e.name))),n()})))})))};if(s){const e=s.callback;s.callback=t=>{e&&e(t),this.properties.currentStyleType=t,t?getStyles(t).then((e=>{e&&(pb_cache[t]=e,p(),n(),l.style.display="block")})):(l.innerHTML="",l.style.display="none")}}return this.addDOMWidget("button","btn",r).getValue=()=>{const e=this.properties.values,t=e.join(",");this.properties.style_positive=e.join(", ");let s=e.map((e=>{const t=pb_cache[this.properties.currentStyleType]?.find((t=>t.name===e));return t?.negative_prompt||""})).filter((e=>e)).join(", ");return this.properties.style_negative=s,t},this.setSize([500,550]),e}}}});
2 |
--------------------------------------------------------------------------------
/web/js/comfly.js:
--------------------------------------------------------------------------------
1 | import{app}from"../../../scripts/app.js";import{helpButton}from"./help_button.js";import{dragHandle}from"./drag_handle.js";import{manageDependenciesButton}from"./Comfly_manager.js";import{chatButton}from"./chat_button.js";const ComflyExtension={name:"Comfly",async init(){console.log("[Comfly]","Extension initialized"),await chatButton.init(),this.createUI()},createUI(){const t=document.createElement("div");t.className="comfly-container",t.style.position="fixed",t.style.left="95px",t.style.bottom="20px",t.style.zIndex="1000",t.style.display="flex",t.style.flexDirection="row",t.style.alignItems="center",document.body.appendChild(t);const e=document.createElement("div");e.style.display="none",e.style.flexDirection="row",e.style.alignItems="center",e.style.marginLeft="10px";const n=dragHandle.createButton(t,e);t.appendChild(n);const o=chatButton.createButton(t);e.appendChild(o);const a=manageDependenciesButton.createButton();e.appendChild(a);const l=helpButton.createButton();e.appendChild(l),t.appendChild(e),"true"===localStorage.getItem("comflyContainerHidden")&&(t.style.display="none")}};app.registerExtension(ComflyExtension);
2 |
--------------------------------------------------------------------------------
/web/js/drag_handle.js:
--------------------------------------------------------------------------------
1 | export const dragHandle={createButton(e,t){const n=document.createElement("div");n.style.position="relative",n.style.cursor="move",n.classList.add("comfly-main-button");const s=document.createElement("button");return s.style.backgroundImage="url('https://api.comfly.chat/wp-content/uploads/2024/09/Comfly-t拷贝-2.png')",s.style.backgroundSize="cover",s.style.backgroundRepeat="no-repeat",s.style.backgroundPosition="center",s.textContent="",s.style.width="38px",s.style.height="38px",s.style.padding="10px",s.style.borderRadius="50%",s.style.margin="5px",s.style.backgroundColor="transparent",s.style.border="none",s.style.boxShadow="0 0 10px 8px rgba(255, 253, 1, 0.7)",s.style.transition="transform 0.2s, box-shadow 0.2s",s.style.animation="flame 1.5s infinite alternate",n.appendChild(s),n.addEventListener("mouseover",(()=>{s.style.transition="transform 0.5s ease",s.style.transform="rotate(360deg)"})),n.addEventListener("mouseout",(()=>{s.style.transform="rotate(0deg)"})),n.addEventListener("mousedown",(()=>{s.style.transform="scale(0.95)",s.style.boxShadow="0 0 6px 5px rgba(255, 253, 1, 0.7)"})),n.addEventListener("mouseup",(()=>{s.style.transform="scale(1)",s.style.boxShadow="0 0 12px 8px rgba(255, 253, 1, 0.7)"})),n.addEventListener("click",(()=>{"none"===t.style.display?this.showContainer(t):this.hideContainer(t)})),this.makeDraggable(e,n),n.style.display="flex",t.style.display="none",n},hideContainer(e){e.style.opacity=0,setTimeout((()=>{e.style.display="none"}),500)},showContainer(e){e.style.display="flex",e.style.opacity=0,e.style.transition="opacity 0.5s ease-in-out",setTimeout((()=>{e.style.opacity=1}),10)},makeDraggable(e,t){let n,s,o,a,r=!1,d=0,i=0;const l=e=>{"touchstart"===e.type?(o=e.touches[0].clientX-d,a=e.touches[0].clientY-i):(o=e.clientX-d,a=e.clientY-i),(e.target===t||t.contains(e.target))&&(r=!0)},c=e=>{o=n,a=s,r=!1},y=t=>{r&&(t.preventDefault(),"touchmove"===t.type?(n=t.touches[0].clientX-o,s=t.touches[0].clientY-a):(n=t.clientX-o,s=t.clientY-a),d=n,i=s,u(n,s,e))},u=(e,t,n)=>{n.style.transform=`translate3d(${e}px, ${t}px, 0)`};let m;const p=(e,t)=>{m||(m=!0,setTimeout((()=>{e(),m=!1}),t))};t.addEventListener("mousedown",l),t.addEventListener("touchstart",l),document.addEventListener("mousemove",(e=>{p((()=>y(e)),10)})),document.addEventListener("touchmove",(e=>{p((()=>y(e)),10)})),document.addEventListener("mouseup",c),document.addEventListener("touchend",c)}};const style=document.createElement("style");style.textContent="\n@keyframes flame {\n 0% {\n box-shadow: 0 0 8px 3px rgba(255, 253, 1, 0.5);\n }\n 100% {\n box-shadow: 0 0 8px 5px rgba(255, 165, 0, 0.7);\n }\n}\n",document.head.append(style);let isAPressed=!1;document.addEventListener("keydown",(e=>{if("a"===e.key.toLowerCase()&&(isAPressed=!0),isAPressed&&"s"===e.key.toLowerCase()){e.preventDefault();const t=document.querySelector(".comfly-main-button");t&&dragHandle.showContainer(t)}if(isAPressed&&"x"===e.key.toLowerCase()){e.preventDefault();const t=document.querySelector(".comfly-main-button");t&&dragHandle.hideContainer(t)}})),document.addEventListener("keyup",(e=>{"a"===e.key.toLowerCase()&&(isAPressed=!1)}));
2 |
--------------------------------------------------------------------------------
/web/js/help_button.js:
--------------------------------------------------------------------------------
1 | export const helpButton = {
2 | createButton() {
3 | const button = document.createElement("button");
4 | button.innerHTML = `
5 | `;
9 | button.style.backgroundSize = "cover";
10 | button.style.backgroundRepeat = "no-repeat";
11 | button.style.backgroundPosition = "center";
12 | button.style.padding = "0px";
13 | button.style.borderRadius = "5px";
14 | button.style.margin = "5px";
15 | button.style.backgroundColor = "#007bff00";
16 | button.style.color = "white";
17 | button.style.border = "none";
18 | button.style.cursor = "pointer";
19 | button.addEventListener("click", () => {
20 | this.openHelpWindow();
21 | });
22 | return button;
23 | },
24 | openHelpWindow() {
25 | const helpWindow = window.open("https://comfly.chat", "_blank", "width=800,height=600,resizable=yes,menubar=no,location=no,status=no");
26 |
27 | // Remove default window border and margin
28 | helpWindow.document.body.style.margin = "0";
29 | helpWindow.document.body.style.padding = "0";
30 | helpWindow.document.body.style.overflow = "hidden";
31 | }
32 | };
--------------------------------------------------------------------------------
/web/js/split_image.js:
--------------------------------------------------------------------------------
1 | import{app}from"../../../scripts/app.js";import{$el}from"../../../scripts/ui.js";import{createImageHost}from"../../../scripts/ui/imagePreview.js";app.registerExtension({name:"split_image",async beforeRegisterNodeDef(t,e,o){if("Comfly_split_image"===e.name){t.prototype.outputType="output";const e=t.prototype.onNodeCreated;t.prototype.onNodeCreated=function(){const t=e?.apply(this,arguments),o=createImageHost(this);return this.addCustomWidget(o.el),this.onExecuted=async()=>{const t=await this.invokeMethod("get_image_paths"),e=await Promise.all(t.map((async t=>{const e=await fetch(`http://localhost:8080/view?filename=${encodeURIComponent(t)}`),o=await e.blob();return $el("img",{src:URL.createObjectURL(o),style:{maxWidth:"100%",maxHeight:"100%",objectFit:"contain"}})})));o.updateImages(e),this.setSize([500,500])},t}}}});
--------------------------------------------------------------------------------
/workflow/Comfly Doubao SeedEdit.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "f6ca7c92-3b93-49d0-b107-4bc211e77151",
3 | "revision": 0,
4 | "last_node_id": 4,
5 | "last_link_id": 3,
6 | "nodes": [
7 | {
8 | "id": 2,
9 | "type": "LoadImage",
10 | "pos": [
11 | -106.79183197021484,
12 | -1344.8896484375
13 | ],
14 | "size": [
15 | 355.26800537109375,
16 | 624.8040161132812
17 | ],
18 | "flags": {},
19 | "order": 0,
20 | "mode": 0,
21 | "inputs": [],
22 | "outputs": [
23 | {
24 | "name": "IMAGE",
25 | "type": "IMAGE",
26 | "links": [
27 | 1
28 | ]
29 | },
30 | {
31 | "name": "MASK",
32 | "type": "MASK",
33 | "links": null
34 | }
35 | ],
36 | "properties": {
37 | "cnr_id": "comfy-core",
38 | "ver": "0.3.27",
39 | "Node name for S&R": "LoadImage"
40 | },
41 | "widgets_values": [
42 | "image_fx_ (30).jpg",
43 | "image",
44 | ""
45 | ]
46 | },
47 | {
48 | "id": 3,
49 | "type": "PreviewImage",
50 | "pos": [
51 | 775.0960083007812,
52 | -1335.6016845703125
53 | ],
54 | "size": [
55 | 422.5010070800781,
56 | 596.8040161132812
57 | ],
58 | "flags": {},
59 | "order": 2,
60 | "mode": 0,
61 | "inputs": [
62 | {
63 | "name": "images",
64 | "type": "IMAGE",
65 | "link": 2
66 | }
67 | ],
68 | "outputs": [],
69 | "properties": {
70 | "cnr_id": "comfy-core",
71 | "ver": "0.3.27",
72 | "Node name for S&R": "PreviewImage"
73 | },
74 | "widgets_values": [
75 | ""
76 | ]
77 | },
78 | {
79 | "id": 4,
80 | "type": "ShowText|pysssss",
81 | "pos": [
82 | 1254.6778564453125,
83 | -1332.5792236328125
84 | ],
85 | "size": [
86 | 377.956298828125,
87 | 370.8584899902344
88 | ],
89 | "flags": {},
90 | "order": 3,
91 | "mode": 0,
92 | "inputs": [
93 | {
94 | "name": "text",
95 | "type": "STRING",
96 | "widget": {
97 | "name": "text"
98 | },
99 | "link": 3
100 | }
101 | ],
102 | "outputs": [
103 | {
104 | "name": "STRING",
105 | "shape": 6,
106 | "type": "STRING",
107 | "links": null
108 | }
109 | ],
110 | "properties": {
111 | "Node name for S&R": "ShowText|pysssss"
112 | },
113 | "widgets_values": [
114 | "",
115 | "**SeedEdit Request**\n\nPrompt: 把黑猫变成白猫\nScale: 0.5\nSeed: 1651735202\nTime: 2025-03-25 12:17:44\n\nSuccess!\n\nImage URL: https://p26-aiop-sign.byteimg.com/tos-cn-i-vuqhorh59i/2025032512183166658E08495CC1A2D49F-0~tplv-vuqhorh59i-image.image?rk3s=7f9e702d&x-expires=1742962724&x-signature=3S1CjM3qqtl6hHymu7Fhd4V%2BwoE%3D\n\nVLM Description: 一只穿着水手服滑雪的白猫在雪山上\nRequest ID: 2025032512183166658E08495CC1A2D49F\nProcessing Time: 12.836374606s\n"
116 | ]
117 | },
118 | {
119 | "id": 1,
120 | "type": "ComflySeededit",
121 | "pos": [
122 | 315.6972351074219,
123 | -1337.4764404296875
124 | ],
125 | "size": [
126 | 400,
127 | 276
128 | ],
129 | "flags": {},
130 | "order": 1,
131 | "mode": 0,
132 | "inputs": [
133 | {
134 | "name": "image",
135 | "type": "IMAGE",
136 | "link": 1
137 | }
138 | ],
139 | "outputs": [
140 | {
141 | "name": "edited_image",
142 | "type": "IMAGE",
143 | "links": [
144 | 2
145 | ]
146 | },
147 | {
148 | "name": "response",
149 | "type": "STRING",
150 | "links": [
151 | 3
152 | ]
153 | }
154 | ],
155 | "properties": {
156 | "Node name for S&R": "ComflySeededit"
157 | },
158 | "widgets_values": [
159 | "把黑猫变成白猫",
160 | 0.5,
161 | 1543254865,
162 | "randomize",
163 | true,
164 | "右下角",
165 | "英文",
166 | "ainewsto"
167 | ]
168 | }
169 | ],
170 | "links": [
171 | [
172 | 1,
173 | 2,
174 | 0,
175 | 1,
176 | 0,
177 | "IMAGE"
178 | ],
179 | [
180 | 2,
181 | 1,
182 | 0,
183 | 3,
184 | 0,
185 | "IMAGE"
186 | ],
187 | [
188 | 3,
189 | 1,
190 | 1,
191 | 4,
192 | 0,
193 | "STRING"
194 | ]
195 | ],
196 | "groups": [],
197 | "config": {},
198 | "extra": {
199 | "ds": {
200 | "scale": 0.7513148009015777,
201 | "offset": [
202 | 276.43375323608717,
203 | 1607.2258088279223
204 | ]
205 | }
206 | },
207 | "version": 0.4
208 | }
--------------------------------------------------------------------------------
/workflow/ComflyGeminiAPI.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 5,
3 | "last_link_id": 4,
4 | "nodes": [
5 | {
6 | "id": 2,
7 | "type": "LoadImage",
8 | "pos": [
9 | -694.7574462890625,
10 | -3232.098388671875
11 | ],
12 | "size": [
13 | 315,
14 | 314
15 | ],
16 | "flags": {},
17 | "order": 0,
18 | "mode": 0,
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "name": "IMAGE",
23 | "type": "IMAGE",
24 | "links": [
25 | 1
26 | ]
27 | },
28 | {
29 | "name": "MASK",
30 | "type": "MASK",
31 | "links": null
32 | }
33 | ],
34 | "properties": {
35 | "cnr_id": "comfy-core",
36 | "ver": "0.3.26",
37 | "Node name for S&R": "LoadImage"
38 | },
39 | "widgets_values": [
40 | "1.jpg",
41 | "image"
42 | ]
43 | },
44 | {
45 | "id": 5,
46 | "type": "ShowText|pysssss",
47 | "pos": [
48 | -299.1976013183594,
49 | -3423.918212890625
50 | ],
51 | "size": [
52 | 315,
53 | 100
54 | ],
55 | "flags": {},
56 | "order": 4,
57 | "mode": 0,
58 | "inputs": [
59 | {
60 | "name": "text",
61 | "type": "STRING",
62 | "widget": {
63 | "name": "text"
64 | },
65 | "link": 4
66 | }
67 | ],
68 | "outputs": [
69 | {
70 | "name": "STRING",
71 | "type": "STRING",
72 | "shape": 6,
73 | "links": null
74 | }
75 | ],
76 | "properties": {
77 | "cnr_id": "comfyui-custom-scripts",
78 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
79 | "Node name for S&R": "ShowText|pysssss"
80 | },
81 | "widgets_values": [
82 | "",
83 | "https://oss.ffire.cc/cdn/2025-03-25/gSQ8bUrVtykXaKsTzsUxve.png"
84 | ]
85 | },
86 | {
87 | "id": 3,
88 | "type": "PreviewImage",
89 | "pos": [
90 | 111.88241577148438,
91 | -3226.768310546875
92 | ],
93 | "size": [
94 | 228,
95 | 304
96 | ],
97 | "flags": {},
98 | "order": 2,
99 | "mode": 0,
100 | "inputs": [
101 | {
102 | "name": "images",
103 | "type": "IMAGE",
104 | "link": 2
105 | }
106 | ],
107 | "outputs": [],
108 | "properties": {
109 | "cnr_id": "comfy-core",
110 | "ver": "0.3.26",
111 | "Node name for S&R": "PreviewImage"
112 | }
113 | },
114 | {
115 | "id": 4,
116 | "type": "ShowText|pysssss",
117 | "pos": [
118 | 379.5123596191406,
119 | -3225.048583984375
120 | ],
121 | "size": [
122 | 386.5,
123 | 297.20001220703125
124 | ],
125 | "flags": {},
126 | "order": 3,
127 | "mode": 0,
128 | "inputs": [
129 | {
130 | "name": "text",
131 | "type": "STRING",
132 | "widget": {
133 | "name": "text"
134 | },
135 | "link": 3
136 | }
137 | ],
138 | "outputs": [
139 | {
140 | "name": "STRING",
141 | "type": "STRING",
142 | "shape": 6,
143 | "links": null
144 | }
145 | ],
146 | "properties": {
147 | "cnr_id": "comfyui-custom-scripts",
148 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
149 | "Node name for S&R": "ShowText|pysssss"
150 | },
151 | "widgets_values": [
152 | "",
153 | "**User prompt**: 把背景换成海洋背景\n\n**Response** (2025-03-25 13:22:18):\n"
154 | ]
155 | },
156 | {
157 | "id": 1,
158 | "type": "ComflyGeminiAPI",
159 | "pos": [
160 | -336.70770263671875,
161 | -3221.64990234375
162 | ],
163 | "size": [
164 | 400,
165 | 296
166 | ],
167 | "flags": {},
168 | "order": 1,
169 | "mode": 0,
170 | "inputs": [
171 | {
172 | "name": "object_image",
173 | "type": "IMAGE",
174 | "shape": 7,
175 | "link": 1
176 | },
177 | {
178 | "name": "subject_image",
179 | "type": "IMAGE",
180 | "shape": 7,
181 | "link": null
182 | },
183 | {
184 | "name": "scene_image",
185 | "type": "IMAGE",
186 | "shape": 7,
187 | "link": null
188 | }
189 | ],
190 | "outputs": [
191 | {
192 | "name": "generated_images",
193 | "type": "IMAGE",
194 | "links": [
195 | 2
196 | ],
197 | "slot_index": 0
198 | },
199 | {
200 | "name": "response",
201 | "type": "STRING",
202 | "links": [
203 | 3
204 | ],
205 | "slot_index": 1
206 | },
207 | {
208 | "name": "image_url",
209 | "type": "STRING",
210 | "links": [
211 | 4
212 | ],
213 | "slot_index": 2
214 | }
215 | ],
216 | "properties": {
217 | "cnr_id": "Comfyui_Comfly",
218 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
219 | "Node name for S&R": "ComflyGeminiAPI"
220 | },
221 | "widgets_values": [
222 | "把背景换成海洋背景",
223 | "gemini-2.0-flash-exp-image",
224 | "object_image size",
225 | 1,
226 | 1,
227 | 0.95,
228 | 467600140,
229 | "randomize"
230 | ]
231 | }
232 | ],
233 | "links": [
234 | [
235 | 1,
236 | 2,
237 | 0,
238 | 1,
239 | 0,
240 | "IMAGE"
241 | ],
242 | [
243 | 2,
244 | 1,
245 | 0,
246 | 3,
247 | 0,
248 | "IMAGE"
249 | ],
250 | [
251 | 3,
252 | 1,
253 | 1,
254 | 4,
255 | 0,
256 | "STRING"
257 | ],
258 | [
259 | 4,
260 | 1,
261 | 2,
262 | 5,
263 | 0,
264 | "STRING"
265 | ]
266 | ],
267 | "groups": [],
268 | "config": {},
269 | "extra": {
270 | "ds": {
271 | "scale": 0.9090909090909091,
272 | "offset": [
273 | 886.3175936029033,
274 | 3574.2588311438362
275 | ]
276 | }
277 | },
278 | "version": 0.4
279 | }
--------------------------------------------------------------------------------
/workflow/Comfly_lip_sync.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 10,
3 | "last_link_id": 8,
4 | "nodes": [
5 | {
6 | "id": 4,
7 | "type": "LoadImage",
8 | "pos": [
9 | -161.66549682617188,
10 | -2891.03564453125
11 | ],
12 | "size": [
13 | 329.2816162109375,
14 | 432.9647521972656
15 | ],
16 | "flags": {},
17 | "order": 0,
18 | "mode": 0,
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "name": "IMAGE",
23 | "type": "IMAGE",
24 | "links": [
25 | 1
26 | ]
27 | },
28 | {
29 | "name": "MASK",
30 | "type": "MASK",
31 | "links": null
32 | }
33 | ],
34 | "properties": {
35 | "cnr_id": "comfy-core",
36 | "ver": "0.3.26",
37 | "Node name for S&R": "LoadImage"
38 | },
39 | "widgets_values": [
40 | "leo19000_A_real_Chinese_little_boy_with_rich_facial_expressio_90864af4-95be-4b50-bbf3-13d9bbe5556c_2.png",
41 | "image"
42 | ]
43 | },
44 | {
45 | "id": 7,
46 | "type": "Comfly_lip_sync",
47 | "pos": [
48 | 535.78125,
49 | -3409.396484375
50 | ],
51 | "size": [
52 | 400,
53 | 440
54 | ],
55 | "flags": {},
56 | "order": 4,
57 | "mode": 0,
58 | "inputs": [
59 | {
60 | "name": "video_id",
61 | "type": "STRING",
62 | "widget": {
63 | "name": "video_id"
64 | },
65 | "link": 4
66 | },
67 | {
68 | "name": "task_id",
69 | "type": "STRING",
70 | "widget": {
71 | "name": "task_id"
72 | },
73 | "link": 5
74 | }
75 | ],
76 | "outputs": [
77 | {
78 | "name": "video",
79 | "type": "VIDEO",
80 | "links": [
81 | 6
82 | ],
83 | "slot_index": 0
84 | },
85 | {
86 | "name": "video_url",
87 | "type": "STRING",
88 | "links": [
89 | 7
90 | ],
91 | "slot_index": 1
92 | },
93 | {
94 | "name": "task_id",
95 | "type": "STRING",
96 | "links": [
97 | 8
98 | ],
99 | "slot_index": 2
100 | }
101 | ],
102 | "properties": {
103 | "cnr_id": "Comfyui_Comfly",
104 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
105 | "Node name for S&R": "Comfly_lip_sync"
106 | },
107 | "widgets_values": [
108 | "",
109 | "",
110 | "text2video",
111 | "今天天气真好啊",
112 | "zh",
113 | "阳光少年",
114 | "Sunny",
115 | 1,
116 | 1045320018,
117 | "randomize",
118 | "",
119 | "file",
120 | "",
121 | ""
122 | ]
123 | },
124 | {
125 | "id": 9,
126 | "type": "ShowText|pysssss",
127 | "pos": [
128 | 730.3987426757812,
129 | -3540.884765625
130 | ],
131 | "size": [
132 | 315,
133 | 58
134 | ],
135 | "flags": {},
136 | "order": 6,
137 | "mode": 0,
138 | "inputs": [
139 | {
140 | "name": "text",
141 | "type": "STRING",
142 | "widget": {
143 | "name": "text"
144 | },
145 | "link": 7
146 | }
147 | ],
148 | "outputs": [
149 | {
150 | "name": "STRING",
151 | "type": "STRING",
152 | "shape": 6,
153 | "links": null
154 | }
155 | ],
156 | "properties": {
157 | "cnr_id": "comfyui-custom-scripts",
158 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
159 | "Node name for S&R": "ShowText|pysssss"
160 | },
161 | "widgets_values": [
162 | ""
163 | ]
164 | },
165 | {
166 | "id": 10,
167 | "type": "ShowText|pysssss",
168 | "pos": [
169 | 354.4396057128906,
170 | -3541.42724609375
171 | ],
172 | "size": [
173 | 315,
174 | 58
175 | ],
176 | "flags": {},
177 | "order": 7,
178 | "mode": 0,
179 | "inputs": [
180 | {
181 | "name": "text",
182 | "type": "STRING",
183 | "widget": {
184 | "name": "text"
185 | },
186 | "link": 8
187 | }
188 | ],
189 | "outputs": [
190 | {
191 | "name": "STRING",
192 | "type": "STRING",
193 | "shape": 6,
194 | "links": null
195 | }
196 | ],
197 | "properties": {
198 | "cnr_id": "comfyui-custom-scripts",
199 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
200 | "Node name for S&R": "ShowText|pysssss"
201 | },
202 | "widgets_values": [
203 | ""
204 | ]
205 | },
206 | {
207 | "id": 6,
208 | "type": "ShowText|pysssss",
209 | "pos": [
210 | -35.430030822753906,
211 | -3534.5078125
212 | ],
213 | "size": [
214 | 315,
215 | 58
216 | ],
217 | "flags": {},
218 | "order": 3,
219 | "mode": 0,
220 | "inputs": [
221 | {
222 | "name": "text",
223 | "type": "STRING",
224 | "widget": {
225 | "name": "text"
226 | },
227 | "link": 3
228 | }
229 | ],
230 | "outputs": [
231 | {
232 | "name": "STRING",
233 | "type": "STRING",
234 | "shape": 6,
235 | "links": null
236 | }
237 | ],
238 | "properties": {
239 | "cnr_id": "comfyui-custom-scripts",
240 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
241 | "Node name for S&R": "ShowText|pysssss"
242 | },
243 | "widgets_values": [
244 | ""
245 | ]
246 | },
247 | {
248 | "id": 8,
249 | "type": "Comfly_kling_videoPreview",
250 | "pos": [
251 | 642.485595703125,
252 | -2891.872802734375
253 | ],
254 | "size": [
255 | 432.8571472167969,
256 | 434.2159729003906
257 | ],
258 | "flags": {},
259 | "order": 5,
260 | "mode": 0,
261 | "inputs": [
262 | {
263 | "name": "video",
264 | "type": "VIDEO",
265 | "link": 6
266 | }
267 | ],
268 | "outputs": [],
269 | "properties": {
270 | "cnr_id": "Comfyui_Comfly",
271 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
272 | "Node name for S&R": "Comfly_kling_videoPreview"
273 | },
274 | "widgets_values": []
275 | },
276 | {
277 | "id": 3,
278 | "type": "Comfly_kling_image2video",
279 | "pos": [
280 | 42.41004943847656,
281 | -3406.02099609375
282 | ],
283 | "size": [
284 | 400,
285 | 442
286 | ],
287 | "flags": {},
288 | "order": 1,
289 | "mode": 0,
290 | "inputs": [
291 | {
292 | "name": "image",
293 | "type": "IMAGE",
294 | "link": 1
295 | },
296 | {
297 | "name": "image_tail",
298 | "type": "IMAGE",
299 | "shape": 7,
300 | "link": null
301 | }
302 | ],
303 | "outputs": [
304 | {
305 | "name": "video",
306 | "type": "VIDEO",
307 | "links": [
308 | 2
309 | ],
310 | "slot_index": 0
311 | },
312 | {
313 | "name": "video_url",
314 | "type": "STRING",
315 | "links": [
316 | 3
317 | ],
318 | "slot_index": 1
319 | },
320 | {
321 | "name": "task_id",
322 | "type": "STRING",
323 | "links": [
324 | 5
325 | ],
326 | "slot_index": 2
327 | },
328 | {
329 | "name": "video_id",
330 | "type": "STRING",
331 | "links": [
332 | 4
333 | ]
334 | }
335 | ],
336 | "properties": {
337 | "cnr_id": "Comfyui_Comfly",
338 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
339 | "Node name for S&R": "Comfly_kling_image2video"
340 | },
341 | "widgets_values": [
342 | "小男孩面对镜头笑起来",
343 | "kling-v1-6",
344 | 0.5,
345 | "1:1",
346 | "std",
347 | "5",
348 | 1,
349 | "",
350 | 1537541256,
351 | "randomize",
352 | "none",
353 | 0
354 | ]
355 | },
356 | {
357 | "id": 5,
358 | "type": "Comfly_kling_videoPreview",
359 | "pos": [
360 | 201.4353485107422,
361 | -2892.9912109375
362 | ],
363 | "size": [
364 | 412.43585205078125,
365 | 439.1280212402344
366 | ],
367 | "flags": {},
368 | "order": 2,
369 | "mode": 0,
370 | "inputs": [
371 | {
372 | "name": "video",
373 | "type": "VIDEO",
374 | "link": 2
375 | }
376 | ],
377 | "outputs": [],
378 | "properties": {
379 | "cnr_id": "Comfyui_Comfly",
380 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
381 | "Node name for S&R": "Comfly_kling_videoPreview"
382 | },
383 | "widgets_values": []
384 | }
385 | ],
386 | "links": [
387 | [
388 | 1,
389 | 4,
390 | 0,
391 | 3,
392 | 0,
393 | "IMAGE"
394 | ],
395 | [
396 | 2,
397 | 3,
398 | 0,
399 | 5,
400 | 0,
401 | "VIDEO"
402 | ],
403 | [
404 | 3,
405 | 3,
406 | 1,
407 | 6,
408 | 0,
409 | "STRING"
410 | ],
411 | [
412 | 4,
413 | 3,
414 | 3,
415 | 7,
416 | 0,
417 | "STRING"
418 | ],
419 | [
420 | 5,
421 | 3,
422 | 2,
423 | 7,
424 | 1,
425 | "STRING"
426 | ],
427 | [
428 | 6,
429 | 7,
430 | 0,
431 | 8,
432 | 0,
433 | "VIDEO"
434 | ],
435 | [
436 | 7,
437 | 7,
438 | 1,
439 | 9,
440 | 0,
441 | "STRING"
442 | ],
443 | [
444 | 8,
445 | 7,
446 | 2,
447 | 10,
448 | 0,
449 | "STRING"
450 | ]
451 | ],
452 | "groups": [],
453 | "config": {},
454 | "extra": {
455 | "ds": {
456 | "scale": 0.6830134553650706,
457 | "offset": [
458 | 495.68704270290425,
459 | 3680.28229484384
460 | ]
461 | }
462 | },
463 | "version": 0.4
464 | }
--------------------------------------------------------------------------------
/workflow/Comfly_mj.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 245,
3 | "last_link_id": 294,
4 | "nodes": [
5 | {
6 | "id": 234,
7 | "type": "ShowText|pysssss",
8 | "pos": [
9 | -900,
10 | -960
11 | ],
12 | "size": [
13 | 590.6380615234375,
14 | 217.42532348632812
15 | ],
16 | "flags": {},
17 | "order": 4,
18 | "mode": 0,
19 | "inputs": [
20 | {
21 | "name": "text",
22 | "type": "STRING",
23 | "link": 282,
24 | "widget": {
25 | "name": "text"
26 | }
27 | }
28 | ],
29 | "outputs": [
30 | {
31 | "name": "STRING",
32 | "type": "STRING",
33 | "links": null,
34 | "shape": 6
35 | }
36 | ],
37 | "title": "mjtext_view",
38 | "properties": {
39 | "Node name for S&R": "ShowText|pysssss"
40 | },
41 | "widgets_values": [
42 | "",
43 | "A stylized illustration of a black cat wearing an orange baseball cap with \\\"LEO\\\" in black capital letters. The cat has bright green eyes, a small pink nose, and a simple, smiling mouth. Its fur is textured and slightly rough. The cat is anthropomorphized, with a human-like body, arms, and legs, and is wearing pink swim trunks with a repeating pattern and a logo. The cat is surfing a large, dark teal and green wave with whitecaps, standing on a pink surfboard. The background is a cloudy sky. The illustration style is hand-drawn or painted.\\n , super detail, best quality, 8k --v 6.1 --ar 3:4"
44 | ]
45 | },
46 | {
47 | "id": 237,
48 | "type": "ShowText|pysssss",
49 | "pos": [
50 | -900,
51 | -1189
52 | ],
53 | "size": [
54 | 588.4142456054688,
55 | 154.83099365234375
56 | ],
57 | "flags": {},
58 | "order": 1,
59 | "mode": 0,
60 | "inputs": [
61 | {
62 | "name": "text",
63 | "type": "STRING",
64 | "link": 271,
65 | "widget": {
66 | "name": "text"
67 | }
68 | }
69 | ],
70 | "outputs": [
71 | {
72 | "name": "STRING",
73 | "type": "STRING",
74 | "links": null,
75 | "shape": 6
76 | }
77 | ],
78 | "title": "mjstyle_view",
79 | "properties": {
80 | "Node name for S&R": "ShowText|pysssss"
81 | },
82 | "widgets_values": [
83 | "",
84 | ", super detail, best quality, 8k"
85 | ]
86 | },
87 | {
88 | "id": 127,
89 | "type": "PreviewImage",
90 | "pos": [
91 | -82.0888900756836,
92 | -629
93 | ],
94 | "size": [
95 | 788.0629272460938,
96 | 818.4010620117188
97 | ],
98 | "flags": {},
99 | "order": 6,
100 | "mode": 0,
101 | "inputs": [
102 | {
103 | "name": "images",
104 | "type": "IMAGE",
105 | "link": 292,
106 | "label": "images"
107 | }
108 | ],
109 | "outputs": [],
110 | "properties": {
111 | "Node name for S&R": "PreviewImage"
112 | },
113 | "widgets_values": []
114 | },
115 | {
116 | "id": 220,
117 | "type": "PreviewImage",
118 | "pos": [
119 | -907,
120 | -622
121 | ],
122 | "size": [
123 | 787.3584594726562,
124 | 815.804443359375
125 | ],
126 | "flags": {},
127 | "order": 3,
128 | "mode": 0,
129 | "inputs": [
130 | {
131 | "name": "images",
132 | "type": "IMAGE",
133 | "link": 289
134 | }
135 | ],
136 | "outputs": [],
137 | "properties": {
138 | "Node name for S&R": "PreviewImage"
139 | },
140 | "widgets_values": []
141 | },
142 | {
143 | "id": 236,
144 | "type": "Comfly_mjstyle",
145 | "pos": [
146 | -1468.6104736328125,
147 | -1280.557861328125
148 | ],
149 | "size": [
150 | 515.4985961914062,
151 | 591.5321655273438
152 | ],
153 | "flags": {},
154 | "order": 0,
155 | "mode": 0,
156 | "inputs": [
157 | {
158 | "name": "positive",
159 | "type": "STRING",
160 | "link": null,
161 | "widget": {
162 | "name": "positive"
163 | }
164 | },
165 | {
166 | "name": "negative",
167 | "type": "STRING",
168 | "link": null,
169 | "widget": {
170 | "name": "negative"
171 | }
172 | }
173 | ],
174 | "outputs": [
175 | {
176 | "name": "style_positive",
177 | "type": "STRING",
178 | "links": [
179 | 271,
180 | 279
181 | ],
182 | "slot_index": 0,
183 | "shape": 3
184 | },
185 | {
186 | "name": "style_negative",
187 | "type": "STRING",
188 | "links": [],
189 | "slot_index": 1,
190 | "shape": 3
191 | }
192 | ],
193 | "properties": {
194 | "Node name for S&R": "Comfly_mjstyle",
195 | "values": [
196 | "super detail",
197 | "best quality",
198 | "8k"
199 | ],
200 | "style_positive": "super detail, best quality, 8k",
201 | "style_negative": ""
202 | },
203 | "widgets_values": [
204 | "mj_art",
205 | "",
206 | "",
207 | "super detail,best quality,8k"
208 | ]
209 | },
210 | {
211 | "id": 239,
212 | "type": "Comfly_Mj",
213 | "pos": [
214 | -1475,
215 | -616
216 | ],
217 | "size": [
218 | 523.5960693359375,
219 | 815.4779663085938
220 | ],
221 | "flags": {},
222 | "order": 2,
223 | "mode": 0,
224 | "inputs": [
225 | {
226 | "name": "no",
227 | "type": "STRING",
228 | "link": null,
229 | "widget": {
230 | "name": "no"
231 | }
232 | },
233 | {
234 | "name": "c",
235 | "type": "INT",
236 | "link": null,
237 | "widget": {
238 | "name": "c"
239 | }
240 | },
241 | {
242 | "name": "s",
243 | "type": "INT",
244 | "link": null,
245 | "widget": {
246 | "name": "s"
247 | }
248 | },
249 | {
250 | "name": "iw",
251 | "type": "FLOAT",
252 | "link": null,
253 | "widget": {
254 | "name": "iw"
255 | }
256 | },
257 | {
258 | "name": "r",
259 | "type": "INT",
260 | "link": null,
261 | "widget": {
262 | "name": "r"
263 | }
264 | },
265 | {
266 | "name": "sw",
267 | "type": "INT",
268 | "link": null,
269 | "widget": {
270 | "name": "sw"
271 | }
272 | },
273 | {
274 | "name": "cw",
275 | "type": "INT",
276 | "link": null,
277 | "widget": {
278 | "name": "cw"
279 | }
280 | },
281 | {
282 | "name": "sv",
283 | "type": "COMBO",
284 | "link": null,
285 | "widget": {
286 | "name": "sv"
287 | }
288 | },
289 | {
290 | "name": "cref",
291 | "type": "STRING",
292 | "link": null,
293 | "widget": {
294 | "name": "cref"
295 | }
296 | },
297 | {
298 | "name": "sref",
299 | "type": "STRING",
300 | "link": null,
301 | "slot_index": 9,
302 | "widget": {
303 | "name": "sref"
304 | }
305 | },
306 | {
307 | "name": "positive",
308 | "type": "STRING",
309 | "link": 279,
310 | "widget": {
311 | "name": "positive"
312 | }
313 | }
314 | ],
315 | "outputs": [
316 | {
317 | "name": "image",
318 | "type": "IMAGE",
319 | "links": [
320 | 289
321 | ],
322 | "slot_index": 0,
323 | "shape": 3
324 | },
325 | {
326 | "name": "text",
327 | "type": "STRING",
328 | "links": [
329 | 282
330 | ],
331 | "slot_index": 1,
332 | "shape": 3
333 | },
334 | {
335 | "name": "taskId",
336 | "type": "STRING",
337 | "links": [
338 | 294
339 | ],
340 | "slot_index": 2,
341 | "shape": 3
342 | }
343 | ],
344 | "properties": {
345 | "Node name for S&R": "Comfly_Mj"
346 | },
347 | "widgets_values": [
348 | "relax mode",
349 | "",
350 | "A stylized illustration of a black cat wearing an orange baseball cap with \\\"LEO\\\" in black capital letters. The cat has bright green eyes, a small pink nose, and a simple, smiling mouth. Its fur is textured and slightly rough. The cat is anthropomorphized, with a human-like body, arms, and legs, and is wearing pink swim trunks with a repeating pattern and a logo. The cat is surfing a large, dark teal and green wave with whitecaps, standing on a pink surfboard. The background is a cloudy sky. The illustration style is hand-drawn or painted.\\n",
351 | "v 6.1",
352 | "3:4",
353 | "",
354 | 0,
355 | 0,
356 | 0,
357 | 1,
358 | 0,
359 | 0,
360 | "1",
361 | false,
362 | false,
363 | 2101087553,
364 | "randomize",
365 | "none",
366 | "1851638940",
367 | ""
368 | ]
369 | },
370 | {
371 | "id": 244,
372 | "type": "Comfly_Mju",
373 | "pos": [
374 | 178.3460693359375,
375 | -917.4812622070312
376 | ],
377 | "size": [
378 | 353.06658935546875,
379 | 166.67950439453125
380 | ],
381 | "flags": {},
382 | "order": 5,
383 | "mode": 0,
384 | "inputs": [
385 | {
386 | "name": "taskId",
387 | "type": "STRING",
388 | "link": 294,
389 | "widget": {
390 | "name": "taskId"
391 | }
392 | }
393 | ],
394 | "outputs": [
395 | {
396 | "name": "image",
397 | "type": "IMAGE",
398 | "links": [
399 | 292
400 | ],
401 | "slot_index": 0
402 | },
403 | {
404 | "name": "taskId",
405 | "type": "STRING",
406 | "links": [
407 | 293
408 | ],
409 | "slot_index": 1
410 | }
411 | ],
412 | "properties": {
413 | "Node name for S&R": "Comfly_Mju"
414 | },
415 | "widgets_values": [
416 | "",
417 | true,
418 | false,
419 | false,
420 | false
421 | ]
422 | },
423 | {
424 | "id": 245,
425 | "type": "Comfly_Mjv",
426 | "pos": [
427 | 1005.7476196289062,
428 | -967.2124633789062
429 | ],
430 | "size": [
431 | 374.2900085449219,
432 | 253.6300048828125
433 | ],
434 | "flags": {},
435 | "order": 7,
436 | "mode": 0,
437 | "inputs": [
438 | {
439 | "name": "taskId",
440 | "type": "STRING",
441 | "link": 293,
442 | "widget": {
443 | "name": "taskId"
444 | }
445 | }
446 | ],
447 | "outputs": [
448 | {
449 | "name": "image",
450 | "type": "IMAGE",
451 | "links": [
452 | 291
453 | ],
454 | "slot_index": 0
455 | }
456 | ],
457 | "properties": {
458 | "Node name for S&R": "Comfly_Mjv"
459 | },
460 | "widgets_values": [
461 | "",
462 | false,
463 | false,
464 | false,
465 | 1,
466 | true,
467 | false,
468 | false,
469 | false
470 | ]
471 | },
472 | {
473 | "id": 110,
474 | "type": "PreviewImage",
475 | "pos": [
476 | 760,
477 | -629
478 | ],
479 | "size": [
480 | 852.7117919921875,
481 | 815.7576904296875
482 | ],
483 | "flags": {},
484 | "order": 8,
485 | "mode": 0,
486 | "inputs": [
487 | {
488 | "name": "images",
489 | "type": "IMAGE",
490 | "link": 291,
491 | "label": "images"
492 | }
493 | ],
494 | "outputs": [],
495 | "properties": {
496 | "Node name for S&R": "PreviewImage"
497 | },
498 | "widgets_values": []
499 | }
500 | ],
501 | "links": [
502 | [
503 | 271,
504 | 236,
505 | 0,
506 | 237,
507 | 0,
508 | "STRING"
509 | ],
510 | [
511 | 279,
512 | 236,
513 | 0,
514 | 239,
515 | 10,
516 | "STRING"
517 | ],
518 | [
519 | 282,
520 | 239,
521 | 1,
522 | 234,
523 | 0,
524 | "STRING"
525 | ],
526 | [
527 | 289,
528 | 239,
529 | 0,
530 | 220,
531 | 0,
532 | "IMAGE"
533 | ],
534 | [
535 | 291,
536 | 245,
537 | 0,
538 | 110,
539 | 0,
540 | "IMAGE"
541 | ],
542 | [
543 | 292,
544 | 244,
545 | 0,
546 | 127,
547 | 0,
548 | "IMAGE"
549 | ],
550 | [
551 | 293,
552 | 244,
553 | 1,
554 | 245,
555 | 0,
556 | "STRING"
557 | ],
558 | [
559 | 294,
560 | 239,
561 | 2,
562 | 244,
563 | 0,
564 | "STRING"
565 | ]
566 | ],
567 | "groups": [
568 | {
569 | "id": 1,
570 | "title": "",
571 | "bounding": [
572 | -1503,
573 | -1381,
574 | 3170,
575 | 1682
576 | ],
577 | "color": "#88A",
578 | "font_size": 35,
579 | "flags": {}
580 | },
581 | {
582 | "id": 2,
583 | "title": "🤩Comfly x Midjourney",
584 | "bounding": [
585 | -1503,
586 | -1801,
587 | 3170,
588 | 420
589 | ],
590 | "color": "#e1ff00",
591 | "font_size": 300,
592 | "flags": {}
593 | },
594 | {
595 | "id": 5,
596 | "title": "U1-U4",
597 | "bounding": [
598 | -100,
599 | -1056,
600 | 829,
601 | 1254
602 | ],
603 | "color": "#88A",
604 | "font_size": 24,
605 | "flags": {}
606 | },
607 | {
608 | "id": 6,
609 | "title": "More",
610 | "bounding": [
611 | 747,
612 | -1056,
613 | 881,
614 | 1255
615 | ],
616 | "color": "#88A",
617 | "font_size": 24,
618 | "flags": {}
619 | }
620 | ],
621 | "config": {},
622 | "extra": {
623 | "ds": {
624 | "scale": 0.35049389948139287,
625 | "offset": [
626 | 2033.2303599279435,
627 | 2007.5497715180475
628 | ]
629 | },
630 | "0246.VERSION": [
631 | 0,
632 | 0,
633 | 4
634 | ]
635 | },
636 | "version": 0.4
637 | }
--------------------------------------------------------------------------------
/workflow/comfly-gemmi-editimage.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 11,
3 | "last_link_id": 16,
4 | "nodes": [
5 | {
6 | "id": 9,
7 | "type": "LoadImage",
8 | "pos": [
9 | -311.8957824707031,
10 | -2835.795654296875
11 | ],
12 | "size": [
13 | 331.3999938964844,
14 | 412.9998779296875
15 | ],
16 | "flags": {},
17 | "order": 0,
18 | "mode": 0,
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "name": "IMAGE",
23 | "type": "IMAGE",
24 | "links": [
25 | 14
26 | ],
27 | "slot_index": 0
28 | },
29 | {
30 | "name": "MASK",
31 | "type": "MASK",
32 | "links": null
33 | }
34 | ],
35 | "properties": {
36 | "cnr_id": "comfy-core",
37 | "ver": "0.3.26",
38 | "Node name for S&R": "LoadImage"
39 | },
40 | "widgets_values": [
41 | "1.jpg",
42 | "image"
43 | ]
44 | },
45 | {
46 | "id": 10,
47 | "type": "LoadImage",
48 | "pos": [
49 | 57.1302490234375,
50 | -2834.20263671875
51 | ],
52 | "size": [
53 | 347.2222900390625,
54 | 409.1423034667969
55 | ],
56 | "flags": {},
57 | "order": 1,
58 | "mode": 0,
59 | "inputs": [],
60 | "outputs": [
61 | {
62 | "name": "IMAGE",
63 | "type": "IMAGE",
64 | "links": [
65 | 15
66 | ]
67 | },
68 | {
69 | "name": "MASK",
70 | "type": "MASK",
71 | "links": null
72 | }
73 | ],
74 | "properties": {
75 | "cnr_id": "comfy-core",
76 | "ver": "0.3.26",
77 | "Node name for S&R": "LoadImage"
78 | },
79 | "widgets_values": [
80 | "2.jpg",
81 | "image"
82 | ]
83 | },
84 | {
85 | "id": 11,
86 | "type": "LoadImage",
87 | "pos": [
88 | 436.45709228515625,
89 | -2831.99658203125
90 | ],
91 | "size": [
92 | 351.1669006347656,
93 | 406.27459716796875
94 | ],
95 | "flags": {},
96 | "order": 2,
97 | "mode": 0,
98 | "inputs": [],
99 | "outputs": [
100 | {
101 | "name": "IMAGE",
102 | "type": "IMAGE",
103 | "links": [
104 | 16
105 | ]
106 | },
107 | {
108 | "name": "MASK",
109 | "type": "MASK",
110 | "links": null
111 | }
112 | ],
113 | "properties": {
114 | "cnr_id": "comfy-core",
115 | "ver": "0.3.26",
116 | "Node name for S&R": "LoadImage"
117 | },
118 | "widgets_values": [
119 | "3.jpg",
120 | "image"
121 | ]
122 | },
123 | {
124 | "id": 3,
125 | "type": "ShowText|pysssss",
126 | "pos": [
127 | 488.1055908203125,
128 | -2365.448486328125
129 | ],
130 | "size": [
131 | 359.760009765625,
132 | 407.8718566894531
133 | ],
134 | "flags": {},
135 | "order": 5,
136 | "mode": 0,
137 | "inputs": [
138 | {
139 | "name": "text",
140 | "type": "STRING",
141 | "widget": {
142 | "name": "text"
143 | },
144 | "link": 10
145 | }
146 | ],
147 | "outputs": [
148 | {
149 | "name": "STRING",
150 | "type": "STRING",
151 | "shape": 6,
152 | "links": null
153 | }
154 | ],
155 | "properties": {
156 | "cnr_id": "comfyui-custom-scripts",
157 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
158 | "Node name for S&R": "ShowText|pysssss"
159 | },
160 | "widgets_values": [
161 | "",
162 | "API timeout error: API request timed out after 90 seconds"
163 | ]
164 | },
165 | {
166 | "id": 2,
167 | "type": "PreviewImage",
168 | "pos": [
169 | 79.46993255615234,
170 | -2371.641845703125
171 | ],
172 | "size": [
173 | 363.3907470703125,
174 | 414.07855224609375
175 | ],
176 | "flags": {},
177 | "order": 4,
178 | "mode": 0,
179 | "inputs": [
180 | {
181 | "name": "images",
182 | "type": "IMAGE",
183 | "link": 9
184 | }
185 | ],
186 | "outputs": [],
187 | "properties": {
188 | "cnr_id": "comfy-core",
189 | "ver": "0.3.26",
190 | "Node name for S&R": "PreviewImage"
191 | },
192 | "widgets_values": []
193 | },
194 | {
195 | "id": 8,
196 | "type": "ComflyGeminiAPI",
197 | "pos": [
198 | -371.1843566894531,
199 | -2360.95703125
200 | ],
201 | "size": [
202 | 405.94873046875,
203 | 397.86920166015625
204 | ],
205 | "flags": {},
206 | "order": 3,
207 | "mode": 0,
208 | "inputs": [
209 | {
210 | "name": "object_image",
211 | "type": "IMAGE",
212 | "shape": 7,
213 | "link": 14
214 | },
215 | {
216 | "name": "subject_image",
217 | "type": "IMAGE",
218 | "shape": 7,
219 | "link": 15
220 | },
221 | {
222 | "name": "scene_image",
223 | "type": "IMAGE",
224 | "shape": 7,
225 | "link": 16
226 | }
227 | ],
228 | "outputs": [
229 | {
230 | "name": "generated_images",
231 | "type": "IMAGE",
232 | "links": [
233 | 9
234 | ],
235 | "slot_index": 0
236 | },
237 | {
238 | "name": "response",
239 | "type": "STRING",
240 | "links": [
241 | 10
242 | ],
243 | "slot_index": 1
244 | }
245 | ],
246 | "properties": {
247 | "cnr_id": "Comfyui_Comfly",
248 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
249 | "Node name for S&R": "ComflyGeminiAPI"
250 | },
251 | "widgets_values": [
252 | "以图片3.jpg为背景, 图片2.jpg拿着图片1.jpg的化妆瓶,。",
253 | "gemini-2.0-flash-exp-image",
254 | "1024x1024",
255 | 1,
256 | 1,
257 | 0.95,
258 | 1595151616,
259 | "randomize"
260 | ]
261 | }
262 | ],
263 | "links": [
264 | [
265 | 9,
266 | 8,
267 | 0,
268 | 2,
269 | 0,
270 | "IMAGE"
271 | ],
272 | [
273 | 10,
274 | 8,
275 | 1,
276 | 3,
277 | 0,
278 | "STRING"
279 | ],
280 | [
281 | 14,
282 | 9,
283 | 0,
284 | 8,
285 | 0,
286 | "IMAGE"
287 | ],
288 | [
289 | 15,
290 | 10,
291 | 0,
292 | 8,
293 | 1,
294 | "IMAGE"
295 | ],
296 | [
297 | 16,
298 | 11,
299 | 0,
300 | 8,
301 | 2,
302 | "IMAGE"
303 | ]
304 | ],
305 | "groups": [],
306 | "config": {},
307 | "extra": {
308 | "ds": {
309 | "scale": 0.6830134553650711,
310 | "offset": [
311 | 690.4164246859499,
312 | 3077.7769017139763
313 | ]
314 | }
315 | },
316 | "version": 0.4
317 | }
--------------------------------------------------------------------------------
/workflow/comfly-gpt4o-api.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 23,
3 | "last_link_id": 26,
4 | "nodes": [
5 | {
6 | "id": 7,
7 | "type": "CLIPTextEncode",
8 | "pos": [
9 | 413,
10 | 389
11 | ],
12 | "size": [
13 | 425.27801513671875,
14 | 180.6060791015625
15 | ],
16 | "flags": {},
17 | "order": 6,
18 | "mode": 0,
19 | "inputs": [
20 | {
21 | "name": "clip",
22 | "type": "CLIP",
23 | "link": 5
24 | }
25 | ],
26 | "outputs": [
27 | {
28 | "name": "CONDITIONING",
29 | "type": "CONDITIONING",
30 | "links": [
31 | 6
32 | ],
33 | "slot_index": 0
34 | }
35 | ],
36 | "properties": {
37 | "cnr_id": "comfy-core",
38 | "ver": "0.3.26",
39 | "Node name for S&R": "CLIPTextEncode"
40 | },
41 | "widgets_values": [
42 | "text, watermark"
43 | ]
44 | },
45 | {
46 | "id": 6,
47 | "type": "CLIPTextEncode",
48 | "pos": [
49 | 415,
50 | 186
51 | ],
52 | "size": [
53 | 422.84503173828125,
54 | 164.31304931640625
55 | ],
56 | "flags": {},
57 | "order": 5,
58 | "mode": 0,
59 | "inputs": [
60 | {
61 | "name": "clip",
62 | "type": "CLIP",
63 | "link": 3
64 | }
65 | ],
66 | "outputs": [
67 | {
68 | "name": "CONDITIONING",
69 | "type": "CONDITIONING",
70 | "links": [
71 | 4
72 | ],
73 | "slot_index": 0
74 | }
75 | ],
76 | "properties": {
77 | "cnr_id": "comfy-core",
78 | "ver": "0.3.26",
79 | "Node name for S&R": "CLIPTextEncode"
80 | },
81 | "widgets_values": [
82 | "beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
83 | ]
84 | },
85 | {
86 | "id": 5,
87 | "type": "EmptyLatentImage",
88 | "pos": [
89 | 473,
90 | 609
91 | ],
92 | "size": [
93 | 315,
94 | 106
95 | ],
96 | "flags": {},
97 | "order": 0,
98 | "mode": 0,
99 | "inputs": [],
100 | "outputs": [
101 | {
102 | "name": "LATENT",
103 | "type": "LATENT",
104 | "links": [
105 | 2
106 | ],
107 | "slot_index": 0
108 | }
109 | ],
110 | "properties": {
111 | "cnr_id": "comfy-core",
112 | "ver": "0.3.26",
113 | "Node name for S&R": "EmptyLatentImage"
114 | },
115 | "widgets_values": [
116 | 512,
117 | 512,
118 | 1
119 | ]
120 | },
121 | {
122 | "id": 3,
123 | "type": "KSampler",
124 | "pos": [
125 | 863,
126 | 186
127 | ],
128 | "size": [
129 | 315,
130 | 262
131 | ],
132 | "flags": {},
133 | "order": 8,
134 | "mode": 0,
135 | "inputs": [
136 | {
137 | "name": "model",
138 | "type": "MODEL",
139 | "link": 1
140 | },
141 | {
142 | "name": "positive",
143 | "type": "CONDITIONING",
144 | "link": 4
145 | },
146 | {
147 | "name": "negative",
148 | "type": "CONDITIONING",
149 | "link": 6
150 | },
151 | {
152 | "name": "latent_image",
153 | "type": "LATENT",
154 | "link": 2
155 | }
156 | ],
157 | "outputs": [
158 | {
159 | "name": "LATENT",
160 | "type": "LATENT",
161 | "links": [
162 | 7
163 | ],
164 | "slot_index": 0
165 | }
166 | ],
167 | "properties": {
168 | "cnr_id": "comfy-core",
169 | "ver": "0.3.26",
170 | "Node name for S&R": "KSampler"
171 | },
172 | "widgets_values": [
173 | 156680208700286,
174 | "randomize",
175 | 20,
176 | 8,
177 | "euler",
178 | "normal",
179 | 1
180 | ]
181 | },
182 | {
183 | "id": 8,
184 | "type": "VAEDecode",
185 | "pos": [
186 | 1209,
187 | 188
188 | ],
189 | "size": [
190 | 210,
191 | 46
192 | ],
193 | "flags": {},
194 | "order": 10,
195 | "mode": 0,
196 | "inputs": [
197 | {
198 | "name": "samples",
199 | "type": "LATENT",
200 | "link": 7
201 | },
202 | {
203 | "name": "vae",
204 | "type": "VAE",
205 | "link": 8
206 | }
207 | ],
208 | "outputs": [
209 | {
210 | "name": "IMAGE",
211 | "type": "IMAGE",
212 | "links": [
213 | 9
214 | ],
215 | "slot_index": 0
216 | }
217 | ],
218 | "properties": {
219 | "cnr_id": "comfy-core",
220 | "ver": "0.3.26",
221 | "Node name for S&R": "VAEDecode"
222 | },
223 | "widgets_values": []
224 | },
225 | {
226 | "id": 9,
227 | "type": "SaveImage",
228 | "pos": [
229 | 1451,
230 | 189
231 | ],
232 | "size": [
233 | 210,
234 | 58
235 | ],
236 | "flags": {},
237 | "order": 15,
238 | "mode": 0,
239 | "inputs": [
240 | {
241 | "name": "images",
242 | "type": "IMAGE",
243 | "link": 9
244 | }
245 | ],
246 | "outputs": [],
247 | "properties": {
248 | "cnr_id": "comfy-core",
249 | "ver": "0.3.26"
250 | },
251 | "widgets_values": [
252 | "ComfyUI"
253 | ]
254 | },
255 | {
256 | "id": 4,
257 | "type": "CheckpointLoaderSimple",
258 | "pos": [
259 | 26,
260 | 474
261 | ],
262 | "size": [
263 | 315,
264 | 98
265 | ],
266 | "flags": {},
267 | "order": 1,
268 | "mode": 0,
269 | "inputs": [],
270 | "outputs": [
271 | {
272 | "name": "MODEL",
273 | "type": "MODEL",
274 | "links": [
275 | 1
276 | ],
277 | "slot_index": 0
278 | },
279 | {
280 | "name": "CLIP",
281 | "type": "CLIP",
282 | "links": [
283 | 3,
284 | 5
285 | ],
286 | "slot_index": 1
287 | },
288 | {
289 | "name": "VAE",
290 | "type": "VAE",
291 | "links": [
292 | 8
293 | ],
294 | "slot_index": 2
295 | }
296 | ],
297 | "properties": {
298 | "cnr_id": "comfy-core",
299 | "ver": "0.3.26",
300 | "Node name for S&R": "CheckpointLoaderSimple"
301 | },
302 | "widgets_values": [
303 | "v1-5-pruned-emaonly-fp16.safetensors"
304 | ]
305 | },
306 | {
307 | "id": 12,
308 | "type": "MultiImagesInput",
309 | "pos": [
310 | -3809.592529296875,
311 | -6912.06787109375
312 | ],
313 | "size": [
314 | 210,
315 | 122
316 | ],
317 | "flags": {},
318 | "order": 7,
319 | "mode": 0,
320 | "inputs": [
321 | {
322 | "name": "image_1",
323 | "type": "IMAGE",
324 | "shape": 7,
325 | "link": 11
326 | },
327 | {
328 | "name": "image_2",
329 | "type": "IMAGE",
330 | "shape": 7,
331 | "link": 12
332 | },
333 | {
334 | "name": "image_3",
335 | "type": "IMAGE",
336 | "link": null
337 | }
338 | ],
339 | "outputs": [
340 | {
341 | "name": "images",
342 | "type": "IMAGE",
343 | "links": [
344 | 22
345 | ],
346 | "slot_index": 0
347 | }
348 | ],
349 | "properties": {},
350 | "widgets_values": [
351 | 3,
352 | null
353 | ]
354 | },
355 | {
356 | "id": 17,
357 | "type": "ShowText|pysssss",
358 | "pos": [
359 | -3049.71142578125,
360 | -6695.4765625
361 | ],
362 | "size": [
363 | 315,
364 | 58
365 | ],
366 | "flags": {},
367 | "order": 12,
368 | "mode": 0,
369 | "inputs": [
370 | {
371 | "name": "text",
372 | "type": "STRING",
373 | "widget": {
374 | "name": "text"
375 | },
376 | "link": 24
377 | }
378 | ],
379 | "outputs": [
380 | {
381 | "name": "STRING",
382 | "type": "STRING",
383 | "shape": 6,
384 | "links": null
385 | }
386 | ],
387 | "properties": {
388 | "cnr_id": "comfyui-custom-scripts",
389 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
390 | "Node name for S&R": "ShowText|pysssss"
391 | },
392 | "widgets_values": [
393 | ""
394 | ]
395 | },
396 | {
397 | "id": 16,
398 | "type": "PreviewImage",
399 | "pos": [
400 | -3383.67236328125,
401 | -6687.01025390625
402 | ],
403 | "size": [
404 | 210,
405 | 26
406 | ],
407 | "flags": {},
408 | "order": 11,
409 | "mode": 0,
410 | "inputs": [
411 | {
412 | "name": "images",
413 | "type": "IMAGE",
414 | "link": 23
415 | }
416 | ],
417 | "outputs": [],
418 | "properties": {
419 | "cnr_id": "comfy-core",
420 | "ver": "0.3.26",
421 | "Node name for S&R": "PreviewImage"
422 | },
423 | "widgets_values": []
424 | },
425 | {
426 | "id": 18,
427 | "type": "ShowText|pysssss",
428 | "pos": [
429 | -3541.840576171875,
430 | -6904.86572265625
431 | ],
432 | "size": [
433 | 315,
434 | 58
435 | ],
436 | "flags": {},
437 | "order": 13,
438 | "mode": 0,
439 | "inputs": [
440 | {
441 | "name": "text",
442 | "type": "STRING",
443 | "widget": {
444 | "name": "text"
445 | },
446 | "link": 25
447 | }
448 | ],
449 | "outputs": [
450 | {
451 | "name": "STRING",
452 | "type": "STRING",
453 | "shape": 6,
454 | "links": []
455 | }
456 | ],
457 | "properties": {
458 | "cnr_id": "comfyui-custom-scripts",
459 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
460 | "Node name for S&R": "ShowText|pysssss"
461 | },
462 | "widgets_values": [
463 | ""
464 | ]
465 | },
466 | {
467 | "id": 22,
468 | "type": "ShowText|pysssss",
469 | "pos": [
470 | -3144.32470703125,
471 | -6906.60888671875
472 | ],
473 | "size": [
474 | 315,
475 | 58
476 | ],
477 | "flags": {},
478 | "order": 14,
479 | "mode": 0,
480 | "inputs": [
481 | {
482 | "name": "text",
483 | "type": "STRING",
484 | "widget": {
485 | "name": "text"
486 | },
487 | "link": 26
488 | }
489 | ],
490 | "outputs": [
491 | {
492 | "name": "STRING",
493 | "type": "STRING",
494 | "shape": 6,
495 | "links": null
496 | }
497 | ],
498 | "properties": {
499 | "cnr_id": "comfyui-custom-scripts",
500 | "ver": "2c09d59ab5ac27ac59022832bfde4eeeb9c55825",
501 | "Node name for S&R": "ShowText|pysssss"
502 | },
503 | "widgets_values": [
504 | ""
505 | ]
506 | },
507 | {
508 | "id": 13,
509 | "type": "LoadImage",
510 | "pos": [
511 | -4290.37939453125,
512 | -6890.12744140625
513 | ],
514 | "size": [
515 | 315,
516 | 102
517 | ],
518 | "flags": {},
519 | "order": 2,
520 | "mode": 0,
521 | "inputs": [],
522 | "outputs": [
523 | {
524 | "name": "IMAGE",
525 | "type": "IMAGE",
526 | "links": [
527 | 11
528 | ]
529 | },
530 | {
531 | "name": "MASK",
532 | "type": "MASK",
533 | "links": null
534 | }
535 | ],
536 | "properties": {
537 | "cnr_id": "comfy-core",
538 | "ver": "0.3.26",
539 | "Node name for S&R": "LoadImage"
540 | },
541 | "widgets_values": [
542 | "07cfd7be-fbef-4d11-9408-5076ee9188ac.mp4",
543 | "image"
544 | ]
545 | },
546 | {
547 | "id": 14,
548 | "type": "LoadImage",
549 | "pos": [
550 | -4300.0166015625,
551 | -6664.62255859375
552 | ],
553 | "size": [
554 | 315,
555 | 102
556 | ],
557 | "flags": {},
558 | "order": 3,
559 | "mode": 0,
560 | "inputs": [],
561 | "outputs": [
562 | {
563 | "name": "IMAGE",
564 | "type": "IMAGE",
565 | "links": [
566 | 12
567 | ]
568 | },
569 | {
570 | "name": "MASK",
571 | "type": "MASK",
572 | "links": null
573 | }
574 | ],
575 | "properties": {
576 | "cnr_id": "comfy-core",
577 | "ver": "0.3.26",
578 | "Node name for S&R": "LoadImage"
579 | },
580 | "widgets_values": [
581 | "07cfd7be-fbef-4d11-9408-5076ee9188ac.mp4",
582 | "image"
583 | ]
584 | },
585 | {
586 | "id": 15,
587 | "type": "LoadImage",
588 | "pos": [
589 | -4309.70556640625,
590 | -6385.1572265625
591 | ],
592 | "size": [
593 | 315,
594 | 102
595 | ],
596 | "flags": {},
597 | "order": 4,
598 | "mode": 0,
599 | "inputs": [],
600 | "outputs": [
601 | {
602 | "name": "IMAGE",
603 | "type": "IMAGE",
604 | "links": [
605 | 13
606 | ]
607 | },
608 | {
609 | "name": "MASK",
610 | "type": "MASK",
611 | "links": null
612 | }
613 | ],
614 | "properties": {
615 | "cnr_id": "comfy-core",
616 | "ver": "0.3.26",
617 | "Node name for S&R": "LoadImage"
618 | },
619 | "widgets_values": [
620 | "07cfd7be-fbef-4d11-9408-5076ee9188ac.mp4",
621 | "image"
622 | ]
623 | },
624 | {
625 | "id": 23,
626 | "type": "ComflyChatGPTApi",
627 | "pos": [
628 | -3887.965576171875,
629 | -6679.4189453125
630 | ],
631 | "size": [
632 | 400,
633 | 388
634 | ],
635 | "flags": {},
636 | "order": 9,
637 | "mode": 0,
638 | "inputs": [
639 | {
640 | "name": "files",
641 | "type": "FILES",
642 | "shape": 7,
643 | "link": null
644 | },
645 | {
646 | "name": "images",
647 | "type": "IMAGE",
648 | "shape": 7,
649 | "link": 22
650 | }
651 | ],
652 | "outputs": [
653 | {
654 | "name": "images",
655 | "type": "IMAGE",
656 | "links": [
657 | 23
658 | ],
659 | "slot_index": 0
660 | },
661 | {
662 | "name": "response",
663 | "type": "STRING",
664 | "links": [
665 | 24
666 | ],
667 | "slot_index": 1
668 | },
669 | {
670 | "name": "image_urls",
671 | "type": "STRING",
672 | "links": [
673 | 25
674 | ],
675 | "slot_index": 2
676 | },
677 | {
678 | "name": "prompt",
679 | "type": "STRING",
680 | "links": [
681 | 26
682 | ],
683 | "slot_index": 3
684 | }
685 | ],
686 | "properties": {
687 | "cnr_id": "Comfyui_Comfly",
688 | "ver": "2929caa9b2742aae0b66cb7b3dd69355a8af46b4\n",
689 | "Node name for S&R": "ComflyChatGPTApi"
690 | },
691 | "widgets_values": [
692 | "",
693 | "gpt-4o-image-vip",
694 | "",
695 | 0.7,
696 | 4096,
697 | 1,
698 | 0,
699 | 0,
700 | -1,
701 | "randomize",
702 | 100
703 | ]
704 | }
705 | ],
706 | "links": [
707 | [
708 | 1,
709 | 4,
710 | 0,
711 | 3,
712 | 0,
713 | "MODEL"
714 | ],
715 | [
716 | 2,
717 | 5,
718 | 0,
719 | 3,
720 | 3,
721 | "LATENT"
722 | ],
723 | [
724 | 3,
725 | 4,
726 | 1,
727 | 6,
728 | 0,
729 | "CLIP"
730 | ],
731 | [
732 | 4,
733 | 6,
734 | 0,
735 | 3,
736 | 1,
737 | "CONDITIONING"
738 | ],
739 | [
740 | 5,
741 | 4,
742 | 1,
743 | 7,
744 | 0,
745 | "CLIP"
746 | ],
747 | [
748 | 6,
749 | 7,
750 | 0,
751 | 3,
752 | 2,
753 | "CONDITIONING"
754 | ],
755 | [
756 | 7,
757 | 3,
758 | 0,
759 | 8,
760 | 0,
761 | "LATENT"
762 | ],
763 | [
764 | 8,
765 | 4,
766 | 2,
767 | 8,
768 | 1,
769 | "VAE"
770 | ],
771 | [
772 | 9,
773 | 8,
774 | 0,
775 | 9,
776 | 0,
777 | "IMAGE"
778 | ],
779 | [
780 | 11,
781 | 13,
782 | 0,
783 | 12,
784 | 0,
785 | "IMAGE"
786 | ],
787 | [
788 | 12,
789 | 14,
790 | 0,
791 | 12,
792 | 1,
793 | "IMAGE"
794 | ],
795 | [
796 | 13,
797 | 15,
798 | 0,
799 | 12,
800 | 2,
801 | "IMAGE"
802 | ],
803 | [
804 | 22,
805 | 12,
806 | 0,
807 | 23,
808 | 1,
809 | "IMAGE"
810 | ],
811 | [
812 | 23,
813 | 23,
814 | 0,
815 | 16,
816 | 0,
817 | "IMAGE"
818 | ],
819 | [
820 | 24,
821 | 23,
822 | 1,
823 | 17,
824 | 0,
825 | "STRING"
826 | ],
827 | [
828 | 25,
829 | 23,
830 | 2,
831 | 18,
832 | 0,
833 | "STRING"
834 | ],
835 | [
836 | 26,
837 | 23,
838 | 3,
839 | 22,
840 | 0,
841 | "STRING"
842 | ]
843 | ],
844 | "groups": [],
845 | "config": {},
846 | "extra": {
847 | "ds": {
848 | "scale": 0.8264462809917354,
849 | "offset": [
850 | 4340.5059189280055,
851 | 7102.921197925815
852 | ]
853 | }
854 | },
855 | "version": 0.4
856 | }
857 |
--------------------------------------------------------------------------------
/workflow/comfly-kling-V1.0-text2video.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 23,
3 | "last_link_id": 23,
4 | "nodes": [
5 | {
6 | "id": 12,
7 | "type": "Comfly_kling_text2video",
8 | "pos": [
9 | 508.3873596191406,
10 | 100.78446960449219
11 | ],
12 | "size": [
13 | 391.20001220703125,
14 | 426
15 | ],
16 | "flags": {},
17 | "order": 0,
18 | "mode": 0,
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "name": "video",
23 | "type": "VIDEO",
24 | "links": [
25 | 12
26 | ],
27 | "slot_index": 0
28 | },
29 | {
30 | "name": "video_url",
31 | "type": "STRING",
32 | "links": [
33 | 14
34 | ],
35 | "slot_index": 1
36 | },
37 | {
38 | "name": "task_id",
39 | "type": "STRING",
40 | "links": null
41 | },
42 | {
43 | "name": "video_id",
44 | "type": "STRING",
45 | "links": [
46 | 20
47 | ],
48 | "slot_index": 3
49 | }
50 | ],
51 | "properties": {
52 | "Node name for S&R": "Comfly_kling_text2video"
53 | },
54 | "widgets_values": [
55 | "一只粉色的猫在天上飞",
56 | "kling-v1",
57 | 0.5,
58 | "1:1",
59 | "std",
60 | "5",
61 | 1,
62 | "",
63 | 1806499942,
64 | "randomize",
65 | "none",
66 | 0
67 | ]
68 | },
69 | {
70 | "id": 10,
71 | "type": "ShowText|pysssss",
72 | "pos": [
73 | 516.880126953125,
74 | -64.20478820800781
75 | ],
76 | "size": [
77 | 372.1260986328125,
78 | 87.67584228515625
79 | ],
80 | "flags": {},
81 | "order": 2,
82 | "mode": 0,
83 | "inputs": [
84 | {
85 | "name": "text",
86 | "type": "STRING",
87 | "link": 14,
88 | "widget": {
89 | "name": "text"
90 | }
91 | }
92 | ],
93 | "outputs": [
94 | {
95 | "name": "STRING",
96 | "type": "STRING",
97 | "links": null,
98 | "shape": 6
99 | }
100 | ],
101 | "properties": {
102 | "Node name for S&R": "ShowText|pysssss"
103 | },
104 | "widgets_values": [
105 | ""
106 | ]
107 | },
108 | {
109 | "id": 8,
110 | "type": "Comfly_kling_videoPreview",
111 | "pos": [
112 | 1432.8990478515625,
113 | 24.682924270629883
114 | ],
115 | "size": [
116 | 451.5916748046875,
117 | 480.7630310058594
118 | ],
119 | "flags": {},
120 | "order": 5,
121 | "mode": 0,
122 | "inputs": [
123 | {
124 | "name": "video",
125 | "type": "VIDEO",
126 | "link": 5
127 | }
128 | ],
129 | "outputs": [],
130 | "properties": {
131 | "Node name for S&R": "Comfly_kling_videoPreview"
132 | },
133 | "widgets_values": [
134 | {
135 | "hidden": false,
136 | "paused": false,
137 | "params": {}
138 | }
139 | ]
140 | },
141 | {
142 | "id": 23,
143 | "type": "ShowText|pysssss",
144 | "pos": [
145 | 521.4036254882812,
146 | -212.97837829589844
147 | ],
148 | "size": [
149 | 360.48590087890625,
150 | 72.04704284667969
151 | ],
152 | "flags": {},
153 | "order": 3,
154 | "mode": 0,
155 | "inputs": [
156 | {
157 | "name": "text",
158 | "type": "STRING",
159 | "link": 20,
160 | "widget": {
161 | "name": "text"
162 | }
163 | }
164 | ],
165 | "outputs": [
166 | {
167 | "name": "STRING",
168 | "type": "STRING",
169 | "links": [
170 | 23
171 | ],
172 | "shape": 6,
173 | "slot_index": 0
174 | }
175 | ],
176 | "properties": {
177 | "Node name for S&R": "ShowText|pysssss"
178 | },
179 | "widgets_values": [
180 | ""
181 | ]
182 | },
183 | {
184 | "id": 7,
185 | "type": "Comfly_video_extend",
186 | "pos": [
187 | 1003.2788696289062,
188 | -194.6882781982422
189 | ],
190 | "size": [
191 | 318.736083984375,
192 | 114.19198608398438
193 | ],
194 | "flags": {},
195 | "order": 4,
196 | "mode": 0,
197 | "inputs": [
198 | {
199 | "name": "video_id",
200 | "type": "STRING",
201 | "link": 23,
202 | "widget": {
203 | "name": "video_id"
204 | }
205 | }
206 | ],
207 | "outputs": [
208 | {
209 | "name": "video",
210 | "type": "VIDEO",
211 | "links": [
212 | 5
213 | ],
214 | "slot_index": 0
215 | },
216 | {
217 | "name": "video_id",
218 | "type": "STRING",
219 | "links": [
220 | 6
221 | ],
222 | "slot_index": 1
223 | }
224 | ],
225 | "properties": {
226 | "Node name for S&R": "Comfly_video_extend"
227 | },
228 | "widgets_values": [
229 | "32b6cb35-1dbc-44d3-b1e8-494a70f1624e",
230 | ""
231 | ]
232 | },
233 | {
234 | "id": 6,
235 | "type": "Comfly_kling_videoPreview",
236 | "pos": [
237 | 953.5421752929688,
238 | 18.28876304626465
239 | ],
240 | "size": [
241 | 438.3602294921875,
242 | 496.71234130859375
243 | ],
244 | "flags": {},
245 | "order": 1,
246 | "mode": 0,
247 | "inputs": [
248 | {
249 | "name": "video",
250 | "type": "VIDEO",
251 | "link": 12
252 | }
253 | ],
254 | "outputs": [],
255 | "properties": {
256 | "Node name for S&R": "Comfly_kling_videoPreview"
257 | },
258 | "widgets_values": []
259 | },
260 | {
261 | "id": 9,
262 | "type": "ShowText|pysssss",
263 | "pos": [
264 | 1444.631103515625,
265 | -190.37957763671875
266 | ],
267 | "size": [
268 | 334.32611083984375,
269 | 76
270 | ],
271 | "flags": {},
272 | "order": 6,
273 | "mode": 0,
274 | "inputs": [
275 | {
276 | "name": "text",
277 | "type": "STRING",
278 | "link": 6,
279 | "widget": {
280 | "name": "text"
281 | }
282 | }
283 | ],
284 | "outputs": [
285 | {
286 | "name": "STRING",
287 | "type": "STRING",
288 | "links": null,
289 | "shape": 6
290 | }
291 | ],
292 | "properties": {
293 | "Node name for S&R": "ShowText|pysssss"
294 | },
295 | "widgets_values": [
296 | "",
297 | "ca4bc1e3-cb5a-4490-a479-7b9e054cb847"
298 | ]
299 | }
300 | ],
301 | "links": [
302 | [
303 | 5,
304 | 7,
305 | 0,
306 | 8,
307 | 0,
308 | "VIDEO"
309 | ],
310 | [
311 | 6,
312 | 7,
313 | 1,
314 | 9,
315 | 0,
316 | "STRING"
317 | ],
318 | [
319 | 12,
320 | 12,
321 | 0,
322 | 6,
323 | 0,
324 | "VIDEO"
325 | ],
326 | [
327 | 14,
328 | 12,
329 | 1,
330 | 10,
331 | 0,
332 | "STRING"
333 | ],
334 | [
335 | 20,
336 | 12,
337 | 3,
338 | 23,
339 | 0,
340 | "STRING"
341 | ],
342 | [
343 | 23,
344 | 23,
345 | 0,
346 | 7,
347 | 0,
348 | "STRING"
349 | ]
350 | ],
351 | "groups": [],
352 | "config": {},
353 | "extra": {
354 | "ds": {
355 | "scale": 0.6830134553650705,
356 | "offset": [
357 | -302.39015550392986,
358 | 383.69692544634916
359 | ]
360 | }
361 | },
362 | "version": 0.4
363 | }
--------------------------------------------------------------------------------
/workflow/comfly-kling-image2video.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 28,
3 | "last_link_id": 31,
4 | "nodes": [
5 | {
6 | "id": 26,
7 | "type": "LoadImage",
8 | "pos": [
9 | 148.95240783691406,
10 | 175.29641723632812
11 | ],
12 | "size": [
13 | 325.5415344238281,
14 | 335.0829772949219
15 | ],
16 | "flags": {},
17 | "order": 0,
18 | "mode": 0,
19 | "inputs": [],
20 | "outputs": [
21 | {
22 | "name": "IMAGE",
23 | "type": "IMAGE",
24 | "links": [
25 | 28
26 | ]
27 | },
28 | {
29 | "name": "MASK",
30 | "type": "MASK",
31 | "links": null
32 | }
33 | ],
34 | "properties": {
35 | "Node name for S&R": "LoadImage"
36 | },
37 | "widgets_values": [
38 | "ComfyUI_temp_cqaav_00001_.png",
39 | "image"
40 | ]
41 | },
42 | {
43 | "id": 25,
44 | "type": "LoadImage",
45 | "pos": [
46 | 150.36965942382812,
47 | -215.4776611328125
48 | ],
49 | "size": [
50 | 315,
51 | 314
52 | ],
53 | "flags": {},
54 | "order": 1,
55 | "mode": 0,
56 | "inputs": [],
57 | "outputs": [
58 | {
59 | "name": "IMAGE",
60 | "type": "IMAGE",
61 | "links": [
62 | 27
63 | ]
64 | },
65 | {
66 | "name": "MASK",
67 | "type": "MASK",
68 | "links": null
69 | }
70 | ],
71 | "properties": {
72 | "Node name for S&R": "LoadImage"
73 | },
74 | "widgets_values": [
75 | "ComfyUI_temp_cqaav_00005_.png",
76 | "image"
77 | ]
78 | },
79 | {
80 | "id": 24,
81 | "type": "Comfly_kling_image2video",
82 | "pos": [
83 | 517.8356323242188,
84 | -52.11933135986328
85 | ],
86 | "size": [
87 | 418.4242858886719,
88 | 564.8904418945312
89 | ],
90 | "flags": {},
91 | "order": 2,
92 | "mode": 0,
93 | "inputs": [
94 | {
95 | "name": "image",
96 | "type": "IMAGE",
97 | "link": 27
98 | },
99 | {
100 | "name": "image_tail",
101 | "type": "IMAGE",
102 | "link": 28
103 | }
104 | ],
105 | "outputs": [
106 | {
107 | "name": "video",
108 | "type": "VIDEO",
109 | "links": [
110 | 24
111 | ],
112 | "slot_index": 0
113 | },
114 | {
115 | "name": "video_url",
116 | "type": "STRING",
117 | "links": [
118 | 25
119 | ],
120 | "slot_index": 1
121 | },
122 | {
123 | "name": "task_id",
124 | "type": "STRING",
125 | "links": null
126 | },
127 | {
128 | "name": "video_id",
129 | "type": "STRING",
130 | "links": [],
131 | "slot_index": 3
132 | }
133 | ],
134 | "properties": {
135 | "Node name for S&R": "Comfly_kling_image2video"
136 | },
137 | "widgets_values": [
138 | "",
139 | "kling-v1",
140 | 0.5,
141 | "1:1",
142 | "std",
143 | "5",
144 | 1,
145 | "",
146 | 0,
147 | "randomize",
148 | "none",
149 | 0
150 | ]
151 | },
152 | {
153 | "id": 10,
154 | "type": "ShowText|pysssss",
155 | "pos": [
156 | 532.9033813476562,
157 | -211.59860229492188
158 | ],
159 | "size": [
160 | 399.0537414550781,
161 | 96.1793441772461
162 | ],
163 | "flags": {},
164 | "order": 4,
165 | "mode": 0,
166 | "inputs": [
167 | {
168 | "name": "text",
169 | "type": "STRING",
170 | "link": 25,
171 | "widget": {
172 | "name": "text"
173 | }
174 | }
175 | ],
176 | "outputs": [
177 | {
178 | "name": "STRING",
179 | "type": "STRING",
180 | "links": null,
181 | "shape": 6
182 | }
183 | ],
184 | "properties": {
185 | "Node name for S&R": "ShowText|pysssss"
186 | },
187 | "widgets_values": [
188 | ""
189 | ]
190 | },
191 | {
192 | "id": 6,
193 | "type": "Comfly_kling_videoPreview",
194 | "pos": [
195 | 988.9732055664062,
196 | -209.64236450195312
197 | ],
198 | "size": [
199 | 635.3577880859375,
200 | 719.2203979492188
201 | ],
202 | "flags": {},
203 | "order": 3,
204 | "mode": 0,
205 | "inputs": [
206 | {
207 | "name": "video",
208 | "type": "VIDEO",
209 | "link": 24
210 | }
211 | ],
212 | "outputs": [],
213 | "properties": {
214 | "Node name for S&R": "Comfly_kling_videoPreview"
215 | },
216 | "widgets_values": []
217 | }
218 | ],
219 | "links": [
220 | [
221 | 24,
222 | 24,
223 | 0,
224 | 6,
225 | 0,
226 | "VIDEO"
227 | ],
228 | [
229 | 25,
230 | 24,
231 | 1,
232 | 10,
233 | 0,
234 | "STRING"
235 | ],
236 | [
237 | 27,
238 | 25,
239 | 0,
240 | 24,
241 | 0,
242 | "IMAGE"
243 | ],
244 | [
245 | 28,
246 | 26,
247 | 0,
248 | 24,
249 | 1,
250 | "IMAGE"
251 | ]
252 | ],
253 | "groups": [],
254 | "config": {},
255 | "extra": {
256 | "ds": {
257 | "scale": 0.5644739300537774,
258 | "offset": [
259 | 132.6056697981938,
260 | 564.9437977196416
261 | ]
262 | }
263 | },
264 | "version": 0.4
265 | }
--------------------------------------------------------------------------------
/workflow/comfly-kling-text2video.json:
--------------------------------------------------------------------------------
1 | {
2 | "last_node_id": 29,
3 | "last_link_id": 33,
4 | "nodes": [
5 | {
6 | "id": 6,
7 | "type": "Comfly_kling_videoPreview",
8 | "pos": [
9 | 988.9732055664062,
10 | -209.64236450195312
11 | ],
12 | "size": [
13 | 635.3577880859375,
14 | 719.2203979492188
15 | ],
16 | "flags": {},
17 | "order": 1,
18 | "mode": 0,
19 | "inputs": [
20 | {
21 | "name": "video",
22 | "type": "VIDEO",
23 | "link": 32
24 | }
25 | ],
26 | "outputs": [],
27 | "properties": {
28 | "Node name for S&R": "Comfly_kling_videoPreview"
29 | },
30 | "widgets_values": []
31 | },
32 | {
33 | "id": 29,
34 | "type": "Comfly_kling_text2video",
35 | "pos": [
36 | 522.1868896484375,
37 | -53.700565338134766
38 | ],
39 | "size": [
40 | 428.344970703125,
41 | 559.221435546875
42 | ],
43 | "flags": {},
44 | "order": 0,
45 | "mode": 0,
46 | "inputs": [],
47 | "outputs": [
48 | {
49 | "name": "video",
50 | "type": "VIDEO",
51 | "links": [
52 | 32
53 | ],
54 | "slot_index": 0
55 | },
56 | {
57 | "name": "video_url",
58 | "type": "STRING",
59 | "links": [
60 | 33
61 | ],
62 | "slot_index": 1
63 | },
64 | {
65 | "name": "task_id",
66 | "type": "STRING",
67 | "links": null
68 | },
69 | {
70 | "name": "video_id",
71 | "type": "STRING",
72 | "links": null
73 | }
74 | ],
75 | "properties": {
76 | "Node name for S&R": "Comfly_kling_text2video"
77 | },
78 | "widgets_values": [
79 | "",
80 | "kling-v1-6",
81 | 0.5,
82 | "1:1",
83 | "std",
84 | "5",
85 | 1,
86 | "",
87 | 0,
88 | "randomize",
89 | "none",
90 | 0
91 | ]
92 | },
93 | {
94 | "id": 10,
95 | "type": "ShowText|pysssss",
96 | "pos": [
97 | 530.5608520507812,
98 | -211.59860229492188
99 | ],
100 | "size": [
101 | 413.10906982421875,
102 | 86.80906677246094
103 | ],
104 | "flags": {},
105 | "order": 2,
106 | "mode": 0,
107 | "inputs": [
108 | {
109 | "name": "text",
110 | "type": "STRING",
111 | "link": 33,
112 | "widget": {
113 | "name": "text"
114 | }
115 | }
116 | ],
117 | "outputs": [
118 | {
119 | "name": "STRING",
120 | "type": "STRING",
121 | "links": null,
122 | "shape": 6,
123 | "slot_index": 0
124 | }
125 | ],
126 | "properties": {
127 | "Node name for S&R": "ShowText|pysssss"
128 | },
129 | "widgets_values": [
130 | ""
131 | ]
132 | }
133 | ],
134 | "links": [
135 | [
136 | 32,
137 | 29,
138 | 0,
139 | 6,
140 | 0,
141 | "VIDEO"
142 | ],
143 | [
144 | 33,
145 | 29,
146 | 1,
147 | 10,
148 | 0,
149 | "STRING"
150 | ]
151 | ],
152 | "groups": [],
153 | "config": {},
154 | "extra": {
155 | "ds": {
156 | "scale": 0.620921323059155,
157 | "offset": [
158 | -87.07468637203304,
159 | 417.6518882089604
160 | ]
161 | }
162 | },
163 | "version": 0.4
164 | }
--------------------------------------------------------------------------------