Files
2026-02-02 04:37:15 +08:00

198 lines
5.6 KiB
Python

"""
ComfyUI Wireless Data Nodes
Standard: PEP 8
Features: Singleton storage, Type-safe variants (Any, Image, Latent).
"""
import logging
from typing import Any, Dict, Tuple, Optional
# Setup Logger
logger = logging.getLogger("ComfyUI.Wireless")
# Global Singleton Storage
_GLOBAL_CONTEXT: Dict[str, Any] = {}
class GlobalStore:
"""
Singleton class to manage global variable storage safely.
"""
@staticmethod
def set(key: str, value: Any) -> None:
"""Sets a value in the global context."""
if not key:
logger.warning("GlobalStore: Attempted to set a variable with an empty key.")
return
# Strip whitespace to prevent "video_vae " != "video_vae" errors
cleaned_key = key.strip()
if cleaned_key != key:
logger.info(f"GlobalStore: Warning - Key has leading/trailing whitespace. Auto-fixing '{key}' -> '{cleaned_key}'")
_GLOBAL_CONTEXT[cleaned_key] = value
@staticmethod
def get(key: str) -> Any:
"""Retrieves a value from the global context."""
if not key:
raise ValueError("GlobalStore: Key cannot be empty.")
# Strip whitespace to match how it is stored
cleaned_key = key.strip()
if cleaned_key not in _GLOBAL_CONTEXT:
available_keys = list(_GLOBAL_CONTEXT.keys())
raise ValueError(f"Wireless Error: Key '{cleaned_key}' not found. (Input was '{key}').\n"
f"Available keys: {available_keys}\n"
f"Please ensure the 'Set Wireless' node executes BEFORE this 'Get Wireless' node.")
return _GLOBAL_CONTEXT[cleaned_key]
class BaseSetNode:
"""Base class for all Set nodes to share logic."""
FUNCTION = "execute"
CATEGORY = "Wireless"
OUTPUT_NODE = True
# Changed from 'execute' to '_set_value' to avoid signature mismatch in subclasses.
# Subclasses must implement 'execute' with the correct argument names matching INPUT_TYPES.
def _set_value(self, key: str, value: Any) -> Tuple[Any]:
GlobalStore.set(key, value)
return (value,)
class BaseGetNode:
"""Base class for all Get nodes to share logic."""
FUNCTION = "execute"
CATEGORY = "Wireless"
def execute(self, key: str) -> Tuple[Any]:
val = GlobalStore.get(key)
return (val,)
# --- 1. Universal (Any) Nodes ---
class SetNodeAny(BaseSetNode):
"""Sets a global variable of any type."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "var_name", "multiline": False}),
"value": ("*", {}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("value",)
def execute(self, key: str, value: Any) -> Tuple[Any]:
return self._set_value(key, value)
class GetNodeAny(BaseGetNode):
"""Gets a global variable of any type."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "var_name", "multiline": False}),
}
}
RETURN_TYPES = ("*",)
RETURN_NAMES = ("value",)
# --- 2. Image Nodes ---
class SetNodeImage(BaseSetNode):
"""Sets a global variable strictly for Images."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "img_var", "multiline": False}),
"image": ("IMAGE", {}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
def execute(self, key: str, image: Any) -> Tuple[Any]:
return self._set_value(key, image)
class GetNodeImage(BaseGetNode):
"""Gets a global variable strictly for Images."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "img_var", "multiline": False}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
# --- 3. Latent Nodes ---
class SetNodeLatent(BaseSetNode):
"""Sets a global variable strictly for Latents."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "latent_var", "multiline": False}),
"latent": ("LATENT", {}),
}
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("latent",)
def execute(self, key: str, latent: Any) -> Tuple[Any]:
return self._set_value(key, latent)
class GetNodeLatent(BaseGetNode):
"""Gets a global variable strictly for Latents."""
@classmethod
def INPUT_TYPES(cls) -> Dict[str, Any]:
return {
"required": {
"key": ("STRING", {"default": "latent_var", "multiline": False}),
}
}
RETURN_TYPES = ("LATENT",)
RETURN_NAMES = ("latent",)
# --- Registration ---
NODE_CLASS_MAPPINGS = {
"WirelessSetAny": SetNodeAny,
"WirelessGetAny": GetNodeAny,
"WirelessSetImage": SetNodeImage,
"WirelessGetImage": GetNodeImage,
"WirelessSetLatent": SetNodeLatent,
"WirelessGetLatent": GetNodeLatent,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"WirelessSetAny": "Set Wireless (Any)",
"WirelessGetAny": "Get Wireless (Any)",
"WirelessSetImage": "Set Wireless (Image)",
"WirelessGetImage": "Get Wireless (Image)",
"WirelessSetLatent": "Set Wireless (Latent)",
"WirelessGetLatent": "Get Wireless (Latent)",
}