-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
97 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,20 +16,21 @@ | |
@description: SPOUT support for ComfyUI. | ||
@node list: | ||
SpoutReaderNode, SpoutWriterNode | ||
@version: 1.0.2 | ||
@version: 1.0.3 | ||
""" | ||
|
||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] | ||
__author__ = """Alexander G. Morano""" | ||
__email__ = "[email protected]" | ||
__version__ = "1.0.2" | ||
__version__ = "1.0.3" | ||
|
||
import os | ||
import sys | ||
import json | ||
import inspect | ||
import importlib | ||
from pathlib import Path | ||
from types import ModuleType | ||
from typing import Any | ||
|
||
from loguru import logger | ||
|
@@ -48,29 +49,15 @@ | |
|
||
JOV_INTERNAL = os.getenv("JOV_INTERNAL", 'false').strip().lower() in ('true', '1', 't') | ||
|
||
JOV_PACKAGE = "Jovi_Spout" | ||
|
||
# ============================================================================== | ||
# === THERE CAN BE ONLY ONE === | ||
# ============================================================================== | ||
|
||
class Singleton(type): | ||
_instances = {} | ||
|
||
def __call__(cls, *arg, **kw) -> Any: | ||
# If the instance does not exist, create and store it | ||
if cls not in cls._instances: | ||
instance = super().__call__(*arg, **kw) | ||
cls._instances[cls] = instance | ||
return cls._instances[cls] | ||
JOV_PACKAGE = "JOV_SPOUT" | ||
|
||
# ============================================================================== | ||
# === CORE NODES === | ||
# ============================================================================== | ||
|
||
class JOVBaseNode: | ||
NOT_IDEMPOTENT = True | ||
CATEGORY = f"{JOV_PACKAGE.upper()} 📺" | ||
CATEGORY = f"{JOV_PACKAGE} 📺" | ||
RETURN_TYPES = () | ||
FUNCTION = "run" | ||
|
||
|
@@ -99,21 +86,9 @@ def INPUT_TYPES(cls, prompt:bool=False, extra_png:bool=False, dynprompt:bool=Fal | |
data["hidden"]["dynprompt"] = "DYNPROMPT" | ||
return data | ||
|
||
class JOVImageNode(JOVBaseNode): | ||
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK") | ||
RETURN_NAMES = ("RGBA", "RGB", "MASK") | ||
|
||
@classmethod | ||
def INPUT_TYPES(cls) -> dict: | ||
d = super().INPUT_TYPES() | ||
d = deep_merge(d, { | ||
"outputs": { | ||
0: ("IMAGE", {"tooltip":"Full channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output."}), | ||
1: ("IMAGE", {"tooltip":"Three channel [RGB] image. There will be no alpha."}), | ||
2: ("MASK", {"tooltip":"Single channel mask output."}), | ||
} | ||
}) | ||
return d | ||
# ============================================================================== | ||
# === TYPE === | ||
# ============================================================================== | ||
|
||
class AnyType(str): | ||
"""AnyType input wildcard trick taken from pythongossss's: | ||
|
@@ -124,32 +99,32 @@ def __ne__(self, __value: object) -> bool: | |
return False | ||
|
||
JOV_TYPE_ANY = AnyType("*") | ||
JOV_TYPE_IMAGE = JOV_TYPE_ANY | ||
|
||
def deep_merge(d1: dict, d2: dict) -> dict: | ||
""" | ||
Deep merge multiple dictionaries recursively. | ||
Args: | ||
*dicts: Variable number of dictionaries to be merged. | ||
Returns: | ||
dict: Merged dictionary. | ||
""" | ||
for key in d2: | ||
if key in d1: | ||
if isinstance(d1[key], dict) and isinstance(d2[key], dict): | ||
deep_merge(d1[key], d2[key]) | ||
else: | ||
d1[key] = d2[key] | ||
else: | ||
d1[key] = d2[key] | ||
return d1 | ||
|
||
# ============================================================================== | ||
# === NODE LOADER === | ||
# ============================================================================== | ||
|
||
def load_module(name: str) -> None|ModuleType: | ||
module = inspect.getmodule(inspect.stack()[0][0]).__name__ | ||
try: | ||
route = str(name).replace("\\", "/") | ||
route = route.split(f"{module}/core/")[1] | ||
route = route.split('.')[0].replace('/', '.') | ||
except Exception as e: | ||
logger.warning(f"module failed {name}") | ||
logger.warning(str(e)) | ||
return | ||
|
||
try: | ||
module = f"{module}.core.{route}" | ||
module = importlib.import_module(module) | ||
except Exception as e: | ||
logger.warning(f"module failed {module}") | ||
logger.warning(str(e)) | ||
return | ||
|
||
return module | ||
|
||
def loader(): | ||
global NODE_DISPLAY_NAME_MAPPINGS, NODE_CLASS_MAPPINGS | ||
NODE_LIST_MAP = {} | ||
|
@@ -158,27 +133,13 @@ def loader(): | |
if fname.stem.startswith('_'): | ||
continue | ||
|
||
try: | ||
route = str(fname).replace("\\", "/").split(f"{JOV_PACKAGE}/core/")[1] | ||
route = route.split('.')[0].replace('/', '.') | ||
module = f"{JOV_PACKAGE}.core.{route}" | ||
module = importlib.import_module(module) | ||
except Exception as e: | ||
logger.warning(f"module failed {fname}") | ||
logger.warning(str(e)) | ||
if (module := load_module(fname)) is None: | ||
continue | ||
|
||
# check if there is a dynamic register function.... | ||
try: | ||
for class_name, class_def in module.import_dynamic(): | ||
setattr(module, class_name, class_def) | ||
except Exception as e: | ||
pass | ||
|
||
classes = inspect.getmembers(module, inspect.isclass) | ||
for class_name, class_object in classes: | ||
if not class_name.endswith('BaseNode') and hasattr(class_object, 'NAME') and hasattr(class_object, 'CATEGORY'): | ||
name = class_object.NAME | ||
name = f"{class_object.NAME} ({JOV_PACKAGE})" | ||
NODE_DISPLAY_NAME_MAPPINGS[name] = name | ||
NODE_CLASS_MAPPINGS[name] = class_object | ||
desc = class_object.DESCRIPTION if hasattr(class_object, 'DESCRIPTION') else name | ||
|
@@ -189,7 +150,7 @@ def loader(): | |
|
||
keys = NODE_CLASS_MAPPINGS.keys() | ||
for name in keys: | ||
logger.debug(f"✅ {name} :: {NODE_DISPLAY_NAME_MAPPINGS[name]}") | ||
logger.debug(f"✅ {name}") | ||
logger.info(f"{len(keys)} nodes loaded") | ||
|
||
# only do the list on local runs... | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
""" | ||
Jovi_Spout - http://www.github.com/Amorano/Jovi_Spout | ||
Core | ||
""" | ||
|
||
from .. import JOVBaseNode | ||
|
||
# ============================================================================== | ||
# === CORE NODES === | ||
# ============================================================================== | ||
|
||
class JOVImageNode(JOVBaseNode): | ||
RETURN_TYPES = ("IMAGE", "IMAGE", "MASK") | ||
RETURN_NAMES = ("RGBA", "RGB", "MASK") | ||
|
||
@classmethod | ||
def INPUT_TYPES(cls) -> dict: | ||
d = super().INPUT_TYPES() | ||
d = deep_merge(d, { | ||
"outputs": { | ||
0: ("IMAGE", {"tooltip":"Full channel [RGBA] image. If there is an alpha, the image will be masked out with it when using this output."}), | ||
1: ("IMAGE", {"tooltip":"Three channel [RGB] image. There will be no alpha."}), | ||
2: ("MASK", {"tooltip":"Single channel mask output."}), | ||
} | ||
}) | ||
return d | ||
|
||
# ============================================================================== | ||
# === SUPPORT === | ||
# ============================================================================== | ||
|
||
def deep_merge(d1: dict, d2: dict) -> dict: | ||
""" | ||
Deep merge multiple dictionaries recursively. | ||
Args: | ||
*dicts: Variable number of dictionaries to be merged. | ||
Returns: | ||
dict: Merged dictionary. | ||
""" | ||
for key in d2: | ||
if key in d1: | ||
if isinstance(d1[key], dict) and isinstance(d2[key], dict): | ||
deep_merge(d1[key], d2[key]) | ||
else: | ||
d1[key] = d2[key] | ||
else: | ||
d1[key] = d2[key] | ||
return d1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
{ | ||
"SPOUT READER (JOV_SP) \ud83d\udcfa": "Capture frames from Spout streams", | ||
"SPOUT WRITER (JOV_SP) \ud83c\udfa5": "Sends frame(s) to a specified Spout receiver application for real-time video sharing" | ||
"SPOUT READER (JOV_SPOUT)": "Capture frames from Spout streams", | ||
"SPOUT WRITER (JOV_SPOUT)": "Sends frame(s) to a specified Spout receiver application for real-time video sharing" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters