198 lines
5.6 KiB
Python
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)",
|
|
}
|