mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-07-27 16:26:39 +00:00
Merge branch 'js/core-api-framework' into v3-definition
This commit is contained in:
commit
9a3d02eb3a
@ -155,7 +155,6 @@ parser.add_argument("--disable-metadata", action="store_true", help="Disable sav
|
|||||||
parser.add_argument("--disable-all-custom-nodes", action="store_true", help="Disable loading all custom nodes.")
|
parser.add_argument("--disable-all-custom-nodes", action="store_true", help="Disable loading all custom nodes.")
|
||||||
parser.add_argument("--whitelist-custom-nodes", type=str, nargs='+', default=[], help="Specify custom node folders to load even when --disable-all-custom-nodes is enabled.")
|
parser.add_argument("--whitelist-custom-nodes", type=str, nargs='+', default=[], help="Specify custom node folders to load even when --disable-all-custom-nodes is enabled.")
|
||||||
parser.add_argument("--disable-api-nodes", action="store_true", help="Disable loading all api nodes.")
|
parser.add_argument("--disable-api-nodes", action="store_true", help="Disable loading all api nodes.")
|
||||||
parser.add_argument("--generate-api-stubs", action="store_true", help="Generate .pyi stub files for API sync wrappers and exit.")
|
|
||||||
|
|
||||||
parser.add_argument("--multi-user", action="store_true", help="Enables per-user storage.")
|
parser.add_argument("--multi-user", action="store_true", help="Enables per-user storage.")
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import logging
|
|||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
# Add ComfyUI to path so we can import modules
|
# Add ComfyUI to path so we can import modules
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
from comfy_api.internal.async_to_sync import AsyncToSyncConverter
|
from comfy_api.internal.async_to_sync import AsyncToSyncConverter
|
||||||
from comfy_api.version_list import supported_versions
|
from comfy_api.version_list import supported_versions
|
||||||
|
@ -1,2 +1,16 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.input import * # noqa: F403
|
from comfy_api.latest._input import (
|
||||||
|
ImageInput,
|
||||||
|
AudioInput,
|
||||||
|
MaskInput,
|
||||||
|
LatentInput,
|
||||||
|
VideoInput,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ImageInput",
|
||||||
|
"AudioInput",
|
||||||
|
"MaskInput",
|
||||||
|
"LatentInput",
|
||||||
|
"VideoInput",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,14 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.input.basic_types import * # noqa: F403
|
from comfy_api.latest._input.basic_types import (
|
||||||
|
ImageInput,
|
||||||
|
AudioInput,
|
||||||
|
MaskInput,
|
||||||
|
LatentInput,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ImageInput",
|
||||||
|
"AudioInput",
|
||||||
|
"MaskInput",
|
||||||
|
"LatentInput",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.input.video_types import * # noqa: F403
|
from comfy_api.latest._input.video_types import VideoInput
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoInput",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,7 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.input_impl import * # noqa: F403
|
from comfy_api.latest._input_impl import VideoFromFile, VideoFromComponents
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoFromFile",
|
||||||
|
"VideoFromComponents",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.input_impl.video_types import * # noqa: F403
|
from comfy_api.latest._input_impl.video_types import * # noqa: F403
|
||||||
|
@ -398,14 +398,18 @@ class AsyncToSyncConverter:
|
|||||||
origin_name = origin_name.split(".")[-1]
|
origin_name = origin_name.split(".")[-1]
|
||||||
|
|
||||||
# Special handling for types.UnionType (Python 3.10+ pipe operator)
|
# Special handling for types.UnionType (Python 3.10+ pipe operator)
|
||||||
if origin_name == "UnionType":
|
# Convert to old-style Union for compatibility
|
||||||
|
if str(origin) == "<class 'types.UnionType'>" or origin_name == "UnionType":
|
||||||
origin_name = "Union"
|
origin_name = "Union"
|
||||||
|
|
||||||
# Format arguments recursively
|
# Format arguments recursively
|
||||||
if args:
|
if args:
|
||||||
formatted_args = [
|
formatted_args = []
|
||||||
cls._format_type_annotation(arg, type_tracker) for arg in args
|
for arg in args:
|
||||||
]
|
# Track each type in the union
|
||||||
|
if type_tracker:
|
||||||
|
type_tracker.track_type(arg)
|
||||||
|
formatted_args.append(cls._format_type_annotation(arg, type_tracker))
|
||||||
return f"{origin_name}[{', '.join(formatted_args)}]"
|
return f"{origin_name}[{', '.join(formatted_args)}]"
|
||||||
else:
|
else:
|
||||||
return origin_name
|
return origin_name
|
||||||
@ -489,23 +493,27 @@ class AsyncToSyncConverter:
|
|||||||
cls,
|
cls,
|
||||||
sig: inspect.Signature,
|
sig: inspect.Signature,
|
||||||
skip_self: bool = True,
|
skip_self: bool = True,
|
||||||
|
type_hints: Optional[dict] = None,
|
||||||
type_tracker: Optional[TypeTracker] = None,
|
type_tracker: Optional[TypeTracker] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Format method parameters for stub files."""
|
"""Format method parameters for stub files."""
|
||||||
params = []
|
params = []
|
||||||
|
if type_hints is None:
|
||||||
|
type_hints = {}
|
||||||
|
|
||||||
for i, (param_name, param) in enumerate(sig.parameters.items()):
|
for i, (param_name, param) in enumerate(sig.parameters.items()):
|
||||||
if i == 0 and param_name == "self" and skip_self:
|
if i == 0 and param_name == "self" and skip_self:
|
||||||
params.append("self")
|
params.append("self")
|
||||||
else:
|
else:
|
||||||
# Get type annotation
|
# Get type annotation from type hints if available, otherwise from signature
|
||||||
type_str = cls._format_type_annotation(param.annotation, type_tracker)
|
annotation = type_hints.get(param_name, param.annotation)
|
||||||
|
type_str = cls._format_type_annotation(annotation, type_tracker)
|
||||||
|
|
||||||
# Get default value
|
# Get default value
|
||||||
default_str = cls._format_parameter_default(param.default)
|
default_str = cls._format_parameter_default(param.default)
|
||||||
|
|
||||||
# Combine parameter parts
|
# Combine parameter parts
|
||||||
if param.annotation is inspect.Parameter.empty:
|
if annotation is inspect.Parameter.empty:
|
||||||
params.append(f"{param_name}: Any{default_str}")
|
params.append(f"{param_name}: Any{default_str}")
|
||||||
else:
|
else:
|
||||||
params.append(f"{param_name}: {type_str}{default_str}")
|
params.append(f"{param_name}: {type_str}{default_str}")
|
||||||
@ -523,13 +531,21 @@ class AsyncToSyncConverter:
|
|||||||
"""Generate a complete method signature for stub files."""
|
"""Generate a complete method signature for stub files."""
|
||||||
sig = inspect.signature(method)
|
sig = inspect.signature(method)
|
||||||
|
|
||||||
|
# Try to get evaluated type hints to resolve string annotations
|
||||||
|
try:
|
||||||
|
from typing import get_type_hints
|
||||||
|
type_hints = get_type_hints(method)
|
||||||
|
except Exception:
|
||||||
|
# Fallback to empty dict if we can't get type hints
|
||||||
|
type_hints = {}
|
||||||
|
|
||||||
# For async methods, extract the actual return type
|
# For async methods, extract the actual return type
|
||||||
return_annotation = sig.return_annotation
|
return_annotation = type_hints.get('return', sig.return_annotation)
|
||||||
if is_async and inspect.iscoroutinefunction(method):
|
if is_async and inspect.iscoroutinefunction(method):
|
||||||
return_annotation = cls._extract_coroutine_return_type(return_annotation)
|
return_annotation = cls._extract_coroutine_return_type(return_annotation)
|
||||||
|
|
||||||
# Format parameters
|
# Format parameters with type hints
|
||||||
params_str = cls._format_method_parameters(sig, type_tracker=type_tracker)
|
params_str = cls._format_method_parameters(sig, type_hints=type_hints, type_tracker=type_tracker)
|
||||||
|
|
||||||
# Format return type
|
# Format return type
|
||||||
return_type = cls._format_type_annotation(return_annotation, type_tracker)
|
return_type = cls._format_type_annotation(return_annotation, type_tracker)
|
||||||
@ -556,8 +572,18 @@ class AsyncToSyncConverter:
|
|||||||
additional_types = []
|
additional_types = []
|
||||||
|
|
||||||
if module:
|
if module:
|
||||||
|
# Check if module has __all__ defined
|
||||||
|
module_all = getattr(module, "__all__", None)
|
||||||
|
|
||||||
for name, obj in sorted(inspect.getmembers(module)):
|
for name, obj in sorted(inspect.getmembers(module)):
|
||||||
if isinstance(obj, type):
|
if isinstance(obj, type):
|
||||||
|
# Skip if __all__ is defined and this name isn't in it
|
||||||
|
# unless it's already been tracked as used in type annotations
|
||||||
|
if module_all is not None and name not in module_all:
|
||||||
|
# Check if this type was actually used in annotations
|
||||||
|
if name not in type_tracker.discovered_types:
|
||||||
|
continue
|
||||||
|
|
||||||
# Check for NamedTuple
|
# Check for NamedTuple
|
||||||
if issubclass(obj, tuple) and hasattr(obj, "_fields"):
|
if issubclass(obj, tuple) and hasattr(obj, "_fields"):
|
||||||
additional_types.append(name)
|
additional_types.append(name)
|
||||||
@ -636,9 +662,17 @@ class AsyncToSyncConverter:
|
|||||||
try:
|
try:
|
||||||
init_method = getattr(attr, "__init__")
|
init_method = getattr(attr, "__init__")
|
||||||
init_sig = inspect.signature(init_method)
|
init_sig = inspect.signature(init_method)
|
||||||
|
|
||||||
|
# Try to get type hints
|
||||||
|
try:
|
||||||
|
from typing import get_type_hints
|
||||||
|
init_hints = get_type_hints(init_method)
|
||||||
|
except Exception:
|
||||||
|
init_hints = {}
|
||||||
|
|
||||||
# Format parameters
|
# Format parameters
|
||||||
params_str = cls._format_method_parameters(
|
params_str = cls._format_method_parameters(
|
||||||
init_sig, type_tracker=type_tracker
|
init_sig, type_hints=init_hints, type_tracker=type_tracker
|
||||||
)
|
)
|
||||||
# Add __init__ docstring if available (before the method)
|
# Add __init__ docstring if available (before the method)
|
||||||
if hasattr(init_method, "__doc__") and init_method.__doc__:
|
if hasattr(init_method, "__doc__") and init_method.__doc__:
|
||||||
@ -790,9 +824,17 @@ class AsyncToSyncConverter:
|
|||||||
try:
|
try:
|
||||||
init_method = async_class.__init__
|
init_method = async_class.__init__
|
||||||
init_signature = inspect.signature(init_method)
|
init_signature = inspect.signature(init_method)
|
||||||
|
|
||||||
|
# Try to get type hints for __init__
|
||||||
|
try:
|
||||||
|
from typing import get_type_hints
|
||||||
|
init_hints = get_type_hints(init_method)
|
||||||
|
except Exception:
|
||||||
|
init_hints = {}
|
||||||
|
|
||||||
# Format parameters
|
# Format parameters
|
||||||
params_str = cls._format_method_parameters(
|
params_str = cls._format_method_parameters(
|
||||||
init_signature, type_tracker=type_tracker
|
init_signature, type_hints=init_hints, type_tracker=type_tracker
|
||||||
)
|
)
|
||||||
# Add __init__ docstring if available (before the method)
|
# Add __init__ docstring if available (before the method)
|
||||||
if hasattr(init_method, "__doc__") and init_method.__doc__:
|
if hasattr(init_method, "__doc__") and init_method.__doc__:
|
||||||
@ -875,18 +917,21 @@ class AsyncToSyncConverter:
|
|||||||
for attr_name, attr_type in sorted(all_annotations.items()):
|
for attr_name, attr_type in sorted(all_annotations.items()):
|
||||||
for class_name, class_type in class_attributes:
|
for class_name, class_type in class_attributes:
|
||||||
# If the class type matches the annotated type
|
# If the class type matches the annotated type
|
||||||
if attr_type == class_type or (
|
if (
|
||||||
hasattr(attr_type, "__name__")
|
attr_type == class_type
|
||||||
and attr_type.__name__ == class_name
|
or (hasattr(attr_type, "__name__") and attr_type.__name__ == class_name)
|
||||||
|
or (isinstance(attr_type, str) and attr_type == class_name)
|
||||||
):
|
):
|
||||||
attribute_mappings[class_name] = attr_name
|
attribute_mappings[class_name] = attr_name
|
||||||
|
|
||||||
# Remove the extra checking - annotations should be sufficient
|
# Remove the extra checking - annotations should be sufficient
|
||||||
|
|
||||||
# Add the attribute declarations with proper names
|
# Add the attribute declarations with proper names
|
||||||
for class_name, _ in class_attributes:
|
for class_name, class_type in class_attributes:
|
||||||
# Use the attribute name if found in mappings, otherwise use class name
|
# Check if there's a mapping from annotation
|
||||||
attr_name = attribute_mappings.get(class_name, class_name)
|
attr_name = attribute_mappings.get(class_name, class_name)
|
||||||
|
# Use the annotation name if it exists, even if the attribute doesn't exist yet
|
||||||
|
# This is because the attribute might be created at runtime
|
||||||
stub_content.append(f" {attr_name}: {class_name}Sync")
|
stub_content.append(f" {attr_name}: {class_name}Sync")
|
||||||
|
|
||||||
stub_content.append("") # Add a final newline
|
stub_content.append("") # Add a final newline
|
||||||
|
@ -4,12 +4,14 @@ from typing import Type, TYPE_CHECKING
|
|||||||
from comfy_api.internal import ComfyAPIBase
|
from comfy_api.internal import ComfyAPIBase
|
||||||
from comfy_api.internal.singleton import ProxiedSingleton
|
from comfy_api.internal.singleton import ProxiedSingleton
|
||||||
from comfy_api.internal.async_to_sync import create_sync_class
|
from comfy_api.internal.async_to_sync import create_sync_class
|
||||||
from comfy_api.latest.input import ImageInput
|
from comfy_api.latest._input import ImageInput, AudioInput, MaskInput, LatentInput, VideoInput
|
||||||
|
from comfy_api.latest._input_impl import VideoFromFile, VideoFromComponents
|
||||||
|
from comfy_api.latest._util import VideoCodec, VideoContainer, VideoComponents
|
||||||
from comfy_api.latest._io import _IO as io #noqa: F401
|
from comfy_api.latest._io import _IO as io #noqa: F401
|
||||||
from comfy_api.latest._ui import _UI as ui #noqa: F401
|
from comfy_api.latest._ui import _UI as ui #noqa: F401
|
||||||
from comfy_api.latest._resources import _RESOURCES as resources #noqa: F401
|
from comfy_api.latest._resources import _RESOURCES as resources #noqa: F401
|
||||||
from comfy_execution.utils import get_executing_context
|
from comfy_execution.utils import get_executing_context
|
||||||
from comfy_execution.progress import get_progress_state
|
from comfy_execution.progress import get_progress_state, PreviewImageTuple
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@ -43,35 +45,51 @@ class ComfyAPI_latest(ComfyAPIBase):
|
|||||||
raise ValueError("node_id must be provided if not in executing context")
|
raise ValueError("node_id must be provided if not in executing context")
|
||||||
|
|
||||||
# Convert preview_image to PreviewImageTuple if needed
|
# Convert preview_image to PreviewImageTuple if needed
|
||||||
if preview_image is not None:
|
to_display: PreviewImageTuple | Image.Image | ImageInput | None = preview_image
|
||||||
|
if to_display is not None:
|
||||||
# First convert to PIL Image if needed
|
# First convert to PIL Image if needed
|
||||||
if isinstance(preview_image, ImageInput):
|
if isinstance(to_display, ImageInput):
|
||||||
# Convert ImageInput (torch.Tensor) to PIL Image
|
# Convert ImageInput (torch.Tensor) to PIL Image
|
||||||
# Handle tensor shape [B, H, W, C] -> get first image if batch
|
# Handle tensor shape [B, H, W, C] -> get first image if batch
|
||||||
tensor = preview_image
|
tensor = to_display
|
||||||
if len(tensor.shape) == 4:
|
if len(tensor.shape) == 4:
|
||||||
tensor = tensor[0]
|
tensor = tensor[0]
|
||||||
|
|
||||||
# Convert to numpy array and scale to 0-255
|
# Convert to numpy array and scale to 0-255
|
||||||
image_np = (tensor.cpu().numpy() * 255).astype(np.uint8)
|
image_np = (tensor.cpu().numpy() * 255).astype(np.uint8)
|
||||||
preview_image = Image.fromarray(image_np)
|
to_display = Image.fromarray(image_np)
|
||||||
|
|
||||||
if isinstance(preview_image, Image.Image):
|
if isinstance(to_display, Image.Image):
|
||||||
# Detect image format from PIL Image
|
# Detect image format from PIL Image
|
||||||
image_format = preview_image.format if preview_image.format else "JPEG"
|
image_format = to_display.format if to_display.format else "JPEG"
|
||||||
# Use None for preview_size if ignore_size_limit is True
|
# Use None for preview_size if ignore_size_limit is True
|
||||||
preview_size = None if ignore_size_limit else args.preview_size
|
preview_size = None if ignore_size_limit else args.preview_size
|
||||||
preview_image = (image_format, preview_image, preview_size)
|
to_display = (image_format, to_display, preview_size)
|
||||||
|
|
||||||
get_progress_state().update_progress(
|
get_progress_state().update_progress(
|
||||||
node_id=node_id,
|
node_id=node_id,
|
||||||
value=value,
|
value=value,
|
||||||
max_value=max_value,
|
max_value=max_value,
|
||||||
image=preview_image,
|
image=to_display,
|
||||||
)
|
)
|
||||||
|
|
||||||
execution: Execution
|
execution: Execution
|
||||||
|
|
||||||
|
class Input:
|
||||||
|
Image = ImageInput
|
||||||
|
Audio = AudioInput
|
||||||
|
Mask = MaskInput
|
||||||
|
Latent = LatentInput
|
||||||
|
Video = VideoInput
|
||||||
|
|
||||||
|
class InputImpl:
|
||||||
|
VideoFromFile = VideoFromFile
|
||||||
|
VideoFromComponents = VideoFromComponents
|
||||||
|
|
||||||
|
class Types:
|
||||||
|
VideoCodec = VideoCodec
|
||||||
|
VideoContainer = VideoContainer
|
||||||
|
VideoComponents = VideoComponents
|
||||||
|
|
||||||
ComfyAPI = ComfyAPI_latest
|
ComfyAPI = ComfyAPI_latest
|
||||||
|
|
||||||
@ -82,3 +100,10 @@ if TYPE_CHECKING:
|
|||||||
ComfyAPISync: Type[comfy_api.latest.generated.ComfyAPISyncStub.ComfyAPISyncStub]
|
ComfyAPISync: Type[comfy_api.latest.generated.ComfyAPISyncStub.ComfyAPISyncStub]
|
||||||
ComfyAPISync = create_sync_class(ComfyAPI_latest)
|
ComfyAPISync = create_sync_class(ComfyAPI_latest)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ComfyAPI",
|
||||||
|
"ComfyAPISync",
|
||||||
|
"Input",
|
||||||
|
"InputImpl",
|
||||||
|
"Types",
|
||||||
|
]
|
||||||
|
@ -3,13 +3,13 @@ from av.container import InputContainer
|
|||||||
from av.subtitles.stream import SubtitleStream
|
from av.subtitles.stream import SubtitleStream
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from comfy_api.latest.input import AudioInput, VideoInput
|
from comfy_api.latest._input import AudioInput, VideoInput
|
||||||
import av
|
import av
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import torch
|
import torch
|
||||||
from comfy_api.latest.util import VideoContainer, VideoCodec, VideoComponents
|
from comfy_api.latest._util import VideoContainer, VideoCodec, VideoComponents
|
||||||
|
|
||||||
|
|
||||||
def container_to_output_format(container_format: str | None) -> str | None:
|
def container_to_output_format(container_format: str | None) -> str | None:
|
@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from comfy_api.latest.input import ImageInput, AudioInput
|
from comfy_api.latest._input import ImageInput, AudioInput
|
||||||
|
|
||||||
class VideoCodec(str, Enum):
|
class VideoCodec(str, Enum):
|
||||||
AUTO = "auto"
|
AUTO = "auto"
|
@ -15,6 +15,6 @@ class ComfyAPISyncStub:
|
|||||||
|
|
||||||
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
||||||
"""
|
"""
|
||||||
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[tuple[str, Image, Union[int, None]], Image, Tensor, None] = None) -> None: ...
|
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[Image, Tensor, None] = None, ignore_size_limit: bool = False) -> None: ...
|
||||||
|
|
||||||
execution: ExecutionSync
|
execution: ExecutionSync
|
||||||
|
@ -1,2 +1,8 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.util import * # noqa: F403
|
from comfy_api.latest._util import VideoCodec, VideoContainer, VideoComponents
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoCodec",
|
||||||
|
"VideoContainer",
|
||||||
|
"VideoComponents",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,8 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.util import * # noqa: F403
|
from comfy_api.latest._util import VideoContainer, VideoCodec, VideoComponents
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoContainer",
|
||||||
|
"VideoCodec",
|
||||||
|
"VideoComponents",
|
||||||
|
]
|
||||||
|
@ -1,2 +1,12 @@
|
|||||||
# This file only exists for backwards compatibility.
|
# This file only exists for backwards compatibility.
|
||||||
from comfy_api.latest.util.video_types import * # noqa: F403
|
from comfy_api.latest._util.video_types import (
|
||||||
|
VideoContainer,
|
||||||
|
VideoCodec,
|
||||||
|
VideoComponents,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"VideoContainer",
|
||||||
|
"VideoCodec",
|
||||||
|
"VideoComponents",
|
||||||
|
]
|
||||||
|
@ -1,18 +1,42 @@
|
|||||||
from comfy_api.v0_0_2 import ComfyAPIAdapter_v0_0_2
|
from comfy_api.v0_0_2 import (
|
||||||
|
ComfyAPIAdapter_v0_0_2,
|
||||||
|
Input as Input_v0_0_2,
|
||||||
|
InputImpl as InputImpl_v0_0_2,
|
||||||
|
Types as Types_v0_0_2,
|
||||||
|
)
|
||||||
from typing import Type, TYPE_CHECKING
|
from typing import Type, TYPE_CHECKING
|
||||||
from comfy_api.internal.async_to_sync import create_sync_class
|
from comfy_api.internal.async_to_sync import create_sync_class
|
||||||
|
|
||||||
|
|
||||||
# This version only exists to serve as a template for future version adapters.
|
# This version only exists to serve as a template for future version adapters.
|
||||||
# There is no reason anyone should ever use it.
|
# There is no reason anyone should ever use it.
|
||||||
class ComfyAPIAdapter_v0_0_1(ComfyAPIAdapter_v0_0_2):
|
class ComfyAPIAdapter_v0_0_1(ComfyAPIAdapter_v0_0_2):
|
||||||
VERSION = "0.0.1"
|
VERSION = "0.0.1"
|
||||||
STABLE = True
|
STABLE = True
|
||||||
|
|
||||||
|
class Input(Input_v0_0_2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InputImpl(InputImpl_v0_0_2):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Types(Types_v0_0_2):
|
||||||
|
pass
|
||||||
|
|
||||||
ComfyAPI = ComfyAPIAdapter_v0_0_1
|
ComfyAPI = ComfyAPIAdapter_v0_0_1
|
||||||
|
|
||||||
# Create a synchronous version of the API
|
# Create a synchronous version of the API
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from comfy_api.v0_0_1.generated.ComfyAPISyncStub import ComfyAPISyncStub # type: ignore
|
from comfy_api.v0_0_1.generated.ComfyAPISyncStub import ComfyAPISyncStub # type: ignore
|
||||||
|
|
||||||
ComfyAPISync: Type[ComfyAPISyncStub]
|
ComfyAPISync: Type[ComfyAPISyncStub]
|
||||||
|
|
||||||
ComfyAPISync = create_sync_class(ComfyAPIAdapter_v0_0_1)
|
ComfyAPISync = create_sync_class(ComfyAPIAdapter_v0_0_1)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ComfyAPI",
|
||||||
|
"ComfyAPISync",
|
||||||
|
"Input",
|
||||||
|
"InputImpl",
|
||||||
|
"Types",
|
||||||
|
]
|
||||||
|
@ -15,6 +15,6 @@ class ComfyAPISyncStub:
|
|||||||
|
|
||||||
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
||||||
"""
|
"""
|
||||||
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[tuple[str, Image, Union[int, None]], Image, Tensor, None] = None) -> None: ...
|
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[Image, Tensor, None] = None, ignore_size_limit: bool = False) -> None: ...
|
||||||
|
|
||||||
execution: ExecutionSync
|
execution: ExecutionSync
|
||||||
|
@ -1,16 +1,44 @@
|
|||||||
from comfy_api.latest import ComfyAPI_latest
|
from comfy_api.latest import (
|
||||||
|
ComfyAPI_latest,
|
||||||
|
Input as Input_latest,
|
||||||
|
InputImpl as InputImpl_latest,
|
||||||
|
Types as Types_latest,
|
||||||
|
)
|
||||||
from typing import Type, TYPE_CHECKING
|
from typing import Type, TYPE_CHECKING
|
||||||
from comfy_api.internal.async_to_sync import create_sync_class
|
from comfy_api.internal.async_to_sync import create_sync_class
|
||||||
from comfy_api.latest import io, ui, resources #noqa: F401
|
from comfy_api.latest import io, ui, resources #noqa: F401
|
||||||
|
|
||||||
|
|
||||||
class ComfyAPIAdapter_v0_0_2(ComfyAPI_latest):
|
class ComfyAPIAdapter_v0_0_2(ComfyAPI_latest):
|
||||||
VERSION = "0.0.2"
|
VERSION = "0.0.2"
|
||||||
STABLE = False
|
STABLE = False
|
||||||
|
|
||||||
|
|
||||||
|
class Input(Input_latest):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InputImpl(InputImpl_latest):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Types(Types_latest):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
ComfyAPI = ComfyAPIAdapter_v0_0_2
|
ComfyAPI = ComfyAPIAdapter_v0_0_2
|
||||||
|
|
||||||
# Create a synchronous version of the API
|
# Create a synchronous version of the API
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from comfy_api.v0_0_2.generated.ComfyAPISyncStub import ComfyAPISyncStub # type: ignore
|
from comfy_api.v0_0_2.generated.ComfyAPISyncStub import ComfyAPISyncStub # type: ignore
|
||||||
|
|
||||||
ComfyAPISync: Type[ComfyAPISyncStub]
|
ComfyAPISync: Type[ComfyAPISyncStub]
|
||||||
ComfyAPISync = create_sync_class(ComfyAPIAdapter_v0_0_2)
|
ComfyAPISync = create_sync_class(ComfyAPIAdapter_v0_0_2)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"ComfyAPI",
|
||||||
|
"ComfyAPISync",
|
||||||
|
"Input",
|
||||||
|
"InputImpl",
|
||||||
|
"Types",
|
||||||
|
]
|
||||||
|
@ -15,6 +15,6 @@ class ComfyAPISyncStub:
|
|||||||
|
|
||||||
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
Migration from previous API: comfy.utils.PROGRESS_BAR_HOOK
|
||||||
"""
|
"""
|
||||||
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[tuple[str, Image, Union[int, None]], Image, Tensor, None] = None) -> None: ...
|
def set_progress(self, value: float, max_value: float, node_id: Union[str, None] = None, preview_image: Union[Image, Tensor, None] = None, ignore_size_limit: bool = False) -> None: ...
|
||||||
|
|
||||||
execution: ExecutionSync
|
execution: ExecutionSync
|
||||||
|
@ -8,9 +8,7 @@ import json
|
|||||||
from typing import Optional, Literal
|
from typing import Optional, Literal
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from comfy.comfy_types import IO, FileLocator, ComfyNodeABC
|
from comfy.comfy_types import IO, FileLocator, ComfyNodeABC
|
||||||
from comfy_api.latest.input import ImageInput, AudioInput, VideoInput
|
from comfy_api.latest import Input, InputImpl, Types
|
||||||
from comfy_api.latest.util import VideoContainer, VideoCodec, VideoComponents
|
|
||||||
from comfy_api.latest.input_impl import VideoFromFile, VideoFromComponents
|
|
||||||
from comfy.cli_args import args
|
from comfy.cli_args import args
|
||||||
|
|
||||||
class SaveWEBM:
|
class SaveWEBM:
|
||||||
@ -91,8 +89,8 @@ class SaveVideo(ComfyNodeABC):
|
|||||||
"required": {
|
"required": {
|
||||||
"video": (IO.VIDEO, {"tooltip": "The video to save."}),
|
"video": (IO.VIDEO, {"tooltip": "The video to save."}),
|
||||||
"filename_prefix": ("STRING", {"default": "video/ComfyUI", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes."}),
|
"filename_prefix": ("STRING", {"default": "video/ComfyUI", "tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes."}),
|
||||||
"format": (VideoContainer.as_input(), {"default": "auto", "tooltip": "The format to save the video as."}),
|
"format": (Types.VideoContainer.as_input(), {"default": "auto", "tooltip": "The format to save the video as."}),
|
||||||
"codec": (VideoCodec.as_input(), {"default": "auto", "tooltip": "The codec to use for the video."}),
|
"codec": (Types.VideoCodec.as_input(), {"default": "auto", "tooltip": "The codec to use for the video."}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"hidden": {
|
||||||
"prompt": "PROMPT",
|
"prompt": "PROMPT",
|
||||||
@ -108,7 +106,7 @@ class SaveVideo(ComfyNodeABC):
|
|||||||
CATEGORY = "image/video"
|
CATEGORY = "image/video"
|
||||||
DESCRIPTION = "Saves the input images to your ComfyUI output directory."
|
DESCRIPTION = "Saves the input images to your ComfyUI output directory."
|
||||||
|
|
||||||
def save_video(self, video: VideoInput, filename_prefix, format, codec, prompt=None, extra_pnginfo=None):
|
def save_video(self, video: Input.Video, filename_prefix, format, codec, prompt=None, extra_pnginfo=None):
|
||||||
filename_prefix += self.prefix_append
|
filename_prefix += self.prefix_append
|
||||||
width, height = video.get_dimensions()
|
width, height = video.get_dimensions()
|
||||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||||
@ -127,7 +125,7 @@ class SaveVideo(ComfyNodeABC):
|
|||||||
metadata["prompt"] = prompt
|
metadata["prompt"] = prompt
|
||||||
if len(metadata) > 0:
|
if len(metadata) > 0:
|
||||||
saved_metadata = metadata
|
saved_metadata = metadata
|
||||||
file = f"{filename}_{counter:05}_.{VideoContainer.get_extension(format)}"
|
file = f"{filename}_{counter:05}_.{Types.VideoContainer.get_extension(format)}"
|
||||||
video.save_to(
|
video.save_to(
|
||||||
os.path.join(full_output_folder, file),
|
os.path.join(full_output_folder, file),
|
||||||
format=format,
|
format=format,
|
||||||
@ -163,9 +161,9 @@ class CreateVideo(ComfyNodeABC):
|
|||||||
CATEGORY = "image/video"
|
CATEGORY = "image/video"
|
||||||
DESCRIPTION = "Create a video from images."
|
DESCRIPTION = "Create a video from images."
|
||||||
|
|
||||||
def create_video(self, images: ImageInput, fps: float, audio: Optional[AudioInput] = None):
|
def create_video(self, images: Input.Image, fps: float, audio: Optional[Input.Audio] = None):
|
||||||
return (VideoFromComponents(
|
return (InputImpl.VideoFromComponents(
|
||||||
VideoComponents(
|
Types.VideoComponents(
|
||||||
images=images,
|
images=images,
|
||||||
audio=audio,
|
audio=audio,
|
||||||
frame_rate=Fraction(fps),
|
frame_rate=Fraction(fps),
|
||||||
@ -187,7 +185,7 @@ class GetVideoComponents(ComfyNodeABC):
|
|||||||
CATEGORY = "image/video"
|
CATEGORY = "image/video"
|
||||||
DESCRIPTION = "Extracts all components from a video: frames, audio, and framerate."
|
DESCRIPTION = "Extracts all components from a video: frames, audio, and framerate."
|
||||||
|
|
||||||
def get_components(self, video: VideoInput):
|
def get_components(self, video: Input.Video):
|
||||||
components = video.get_components()
|
components = video.get_components()
|
||||||
|
|
||||||
return (components.images, components.audio, float(components.frame_rate))
|
return (components.images, components.audio, float(components.frame_rate))
|
||||||
@ -208,7 +206,7 @@ class LoadVideo(ComfyNodeABC):
|
|||||||
FUNCTION = "load_video"
|
FUNCTION = "load_video"
|
||||||
def load_video(self, file):
|
def load_video(self, file):
|
||||||
video_path = folder_paths.get_annotated_filepath(file)
|
video_path = folder_paths.get_annotated_filepath(file)
|
||||||
return (VideoFromFile(video_path),)
|
return (InputImpl.VideoFromFile(video_path),)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def IS_CHANGED(cls, file):
|
def IS_CHANGED(cls, file):
|
||||||
|
6
main.py
6
main.py
@ -22,12 +22,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
setup_logger(log_level=args.verbose, use_stdout=args.log_stdout)
|
setup_logger(log_level=args.verbose, use_stdout=args.log_stdout)
|
||||||
|
|
||||||
# Handle --generate-api-stubs early
|
|
||||||
if args.generate_api_stubs:
|
|
||||||
from comfy_api.generate_api_stubs import main as generate_stubs_main
|
|
||||||
generate_stubs_main()
|
|
||||||
sys.exit(0)
|
|
||||||
|
|
||||||
def apply_custom_paths():
|
def apply_custom_paths():
|
||||||
# extra model paths
|
# extra model paths
|
||||||
extra_model_paths_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "extra_model_paths.yaml")
|
extra_model_paths_config_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "extra_model_paths.yaml")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user