mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-02-28 22:51:45 +00:00
Add Load Image Output node (#6790)
* add LoadImageOutput node * add route for input/output/temp files * update node_typing.py * use literal type for image_folder field * mark node as beta
This commit is contained in:
parent
acc152b674
commit
afc85cdeb6
@ -1,8 +1,9 @@
|
|||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from folder_paths import folder_names_and_paths
|
from folder_paths import folder_names_and_paths, get_directory_by_type
|
||||||
from api_server.services.terminal_service import TerminalService
|
from api_server.services.terminal_service import TerminalService
|
||||||
import app.logger
|
import app.logger
|
||||||
|
import os
|
||||||
|
|
||||||
class InternalRoutes:
|
class InternalRoutes:
|
||||||
'''
|
'''
|
||||||
@ -50,6 +51,20 @@ class InternalRoutes:
|
|||||||
response[key] = folder_names_and_paths[key][0]
|
response[key] = folder_names_and_paths[key][0]
|
||||||
return web.json_response(response)
|
return web.json_response(response)
|
||||||
|
|
||||||
|
@self.routes.get('/files/{directory_type}')
|
||||||
|
async def get_files(request: web.Request) -> web.Response:
|
||||||
|
directory_type = request.match_info['directory_type']
|
||||||
|
if directory_type not in ("output", "input", "temp"):
|
||||||
|
return web.json_response({"error": "Invalid directory type"}, status=400)
|
||||||
|
|
||||||
|
directory = get_directory_by_type(directory_type)
|
||||||
|
sorted_files = sorted(
|
||||||
|
(entry for entry in os.scandir(directory) if entry.is_file()),
|
||||||
|
key=lambda entry: -entry.stat().st_mtime
|
||||||
|
)
|
||||||
|
return web.json_response([entry.name for entry in sorted_files], status=200)
|
||||||
|
|
||||||
|
|
||||||
def get_app(self):
|
def get_app(self):
|
||||||
if self._app is None:
|
if self._app is None:
|
||||||
self._app = web.Application()
|
self._app = web.Application()
|
||||||
|
@ -66,6 +66,19 @@ class IO(StrEnum):
|
|||||||
b = frozenset(value.split(","))
|
b = frozenset(value.split(","))
|
||||||
return not (b.issubset(a) or a.issubset(b))
|
return not (b.issubset(a) or a.issubset(b))
|
||||||
|
|
||||||
|
class RemoteInputOptions(TypedDict):
|
||||||
|
route: str
|
||||||
|
"""The route to the remote source."""
|
||||||
|
refresh_button: bool
|
||||||
|
"""Specifies whether to show a refresh button in the UI below the widget."""
|
||||||
|
control_after_refresh: Literal["first", "last"]
|
||||||
|
"""Specifies the control after the refresh button is clicked. If "first", the first item will be automatically selected, and so on."""
|
||||||
|
timeout: int
|
||||||
|
"""The maximum amount of time to wait for a response from the remote source in milliseconds."""
|
||||||
|
max_retries: int
|
||||||
|
"""The maximum number of retries before aborting the request."""
|
||||||
|
refresh: int
|
||||||
|
"""The TTL of the remote input's value in milliseconds. Specifies the interval at which the remote input's value is refreshed."""
|
||||||
|
|
||||||
class InputTypeOptions(TypedDict):
|
class InputTypeOptions(TypedDict):
|
||||||
"""Provides type hinting for the return type of the INPUT_TYPES node function.
|
"""Provides type hinting for the return type of the INPUT_TYPES node function.
|
||||||
@ -113,6 +126,14 @@ class InputTypeOptions(TypedDict):
|
|||||||
# defaultVal: str
|
# defaultVal: str
|
||||||
dynamicPrompts: bool
|
dynamicPrompts: bool
|
||||||
"""Causes the front-end to evaluate dynamic prompts (``STRING``)"""
|
"""Causes the front-end to evaluate dynamic prompts (``STRING``)"""
|
||||||
|
# class InputTypeCombo(InputTypeOptions):
|
||||||
|
image_upload: bool
|
||||||
|
"""Specifies whether the input should have an image upload button and image preview attached to it. Requires that the input's name is `image`."""
|
||||||
|
image_folder: Literal["input", "output", "temp"]
|
||||||
|
"""Specifies which folder to get preview images from if the input has the ``image_upload`` flag.
|
||||||
|
"""
|
||||||
|
remote: RemoteInputOptions
|
||||||
|
"""Specifies the configuration for a remote input."""
|
||||||
|
|
||||||
|
|
||||||
class HiddenInputTypeDict(TypedDict):
|
class HiddenInputTypeDict(TypedDict):
|
||||||
|
32
nodes.py
32
nodes.py
@ -1763,6 +1763,36 @@ class LoadImageMask:
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class LoadImageOutput(LoadImage):
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("COMBO", {
|
||||||
|
"image_upload": True,
|
||||||
|
"image_folder": "output",
|
||||||
|
"remote": {
|
||||||
|
"route": "/internal/files/output",
|
||||||
|
"refresh_button": True,
|
||||||
|
"control_after_refresh": "first",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DESCRIPTION = "Load an image from the output folder. When the refresh button is clicked, the node will update the image list and automatically select the first image, allowing for easy iteration."
|
||||||
|
EXPERIMENTAL = True
|
||||||
|
FUNCTION = "load_image_output"
|
||||||
|
|
||||||
|
def load_image_output(self, image):
|
||||||
|
return self.load_image(f"{image} [output]")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def VALIDATE_INPUTS(s, image):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class ImageScale:
|
class ImageScale:
|
||||||
upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"]
|
upscale_methods = ["nearest-exact", "bilinear", "area", "bicubic", "lanczos"]
|
||||||
crop_methods = ["disabled", "center"]
|
crop_methods = ["disabled", "center"]
|
||||||
@ -1949,6 +1979,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"PreviewImage": PreviewImage,
|
"PreviewImage": PreviewImage,
|
||||||
"LoadImage": LoadImage,
|
"LoadImage": LoadImage,
|
||||||
"LoadImageMask": LoadImageMask,
|
"LoadImageMask": LoadImageMask,
|
||||||
|
"LoadImageOutput": LoadImageOutput,
|
||||||
"ImageScale": ImageScale,
|
"ImageScale": ImageScale,
|
||||||
"ImageScaleBy": ImageScaleBy,
|
"ImageScaleBy": ImageScaleBy,
|
||||||
"ImageInvert": ImageInvert,
|
"ImageInvert": ImageInvert,
|
||||||
@ -2049,6 +2080,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"PreviewImage": "Preview Image",
|
"PreviewImage": "Preview Image",
|
||||||
"LoadImage": "Load Image",
|
"LoadImage": "Load Image",
|
||||||
"LoadImageMask": "Load Image (as Mask)",
|
"LoadImageMask": "Load Image (as Mask)",
|
||||||
|
"LoadImageOutput": "Load Image (from Outputs)",
|
||||||
"ImageScale": "Upscale Image",
|
"ImageScale": "Upscale Image",
|
||||||
"ImageScaleBy": "Upscale Image By",
|
"ImageScaleBy": "Upscale Image By",
|
||||||
"ImageUpscaleWithModel": "Upscale Image (using Model)",
|
"ImageUpscaleWithModel": "Upscale Image (using Model)",
|
||||||
|
Loading…
Reference in New Issue
Block a user