mirror of
https://github.com/Comfy-Org/ComfyUI.git
synced 2026-09-21 13:38:08 -05:00
[Partner Nodes] fix(Tripo): refuse a P2 run whose linked GLB or FBX output would be empty (#16369)
Signed-off-by: bigcat88 <bigcat88@icloud.com>
This commit is contained in:
@@ -55,6 +55,7 @@ from comfy_api_nodes.util import (
|
||||
tensor_to_bytesio,
|
||||
upload_3d_model_to_comfyapi,
|
||||
upload_images_to_comfyapi,
|
||||
validate_output_unlinked,
|
||||
validate_string,
|
||||
)
|
||||
|
||||
@@ -2482,6 +2483,16 @@ def p_series_request_fields(model: dict) -> dict:
|
||||
return fields
|
||||
|
||||
|
||||
def p_series_check_outputs(cls: type[IO.ComfyNode], quad: bool) -> None:
|
||||
empty_name, filled_name = ("GLB", "FBX") if quad else ("FBX", "GLB")
|
||||
validate_output_unlinked(
|
||||
cls,
|
||||
1 if quad else 2,
|
||||
f"quad is {'enabled' if quad else 'disabled'}, so this node delivers {filled_name} only "
|
||||
f"and its {empty_name} output is empty. Connect the {filled_name} output instead",
|
||||
)
|
||||
|
||||
|
||||
async def p_series_generate(cls: type[IO.ComfyNode], path: str, request: TripoPSeriesRequest) -> IO.NodeOutput:
|
||||
response = await sync_op(
|
||||
cls,
|
||||
@@ -2509,13 +2520,14 @@ class TripoPSeriesTextToModelNode(IO.ComfyNode):
|
||||
search_aliases=["tripo p2", "quad mesh", "low poly"],
|
||||
inputs=[p_series_model_input("text")],
|
||||
outputs=model_outputs(legacy=False),
|
||||
hidden=hidden_inputs(),
|
||||
hidden=[*hidden_inputs(), IO.Hidden.dynprompt],
|
||||
is_api_node=True,
|
||||
price_badge=p_series_price_badge(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def execute(cls, model: dict) -> IO.NodeOutput:
|
||||
p_series_check_outputs(cls, model["quad"])
|
||||
prompt = model["prompt"].strip()
|
||||
negative_prompt = (model.get("negative_prompt") or "").strip()
|
||||
validate_string(prompt, min_length=1, max_length=1024)
|
||||
@@ -2542,13 +2554,14 @@ class TripoPSeriesImageToModelNode(IO.ComfyNode):
|
||||
search_aliases=["tripo p2", "quad mesh", "low poly"],
|
||||
inputs=[p_series_model_input("image")],
|
||||
outputs=model_outputs(legacy=False),
|
||||
hidden=hidden_inputs(),
|
||||
hidden=[*hidden_inputs(), IO.Hidden.dynprompt],
|
||||
is_api_node=True,
|
||||
price_badge=p_series_price_badge(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def execute(cls, model: dict) -> IO.NodeOutput:
|
||||
p_series_check_outputs(cls, model["quad"])
|
||||
fields = p_series_request_fields(model)
|
||||
request = TripoPSeriesImageToModelRequest(
|
||||
input=(await upload_images_to_comfyapi(cls, model["image"], max_images=1))[0],
|
||||
@@ -2571,13 +2584,14 @@ class TripoPSeriesMultiviewToModelNode(IO.ComfyNode):
|
||||
search_aliases=["tripo p2", "quad mesh", "low poly"],
|
||||
inputs=[p_series_model_input("multiview")],
|
||||
outputs=model_outputs(legacy=False),
|
||||
hidden=hidden_inputs(),
|
||||
hidden=[*hidden_inputs(), IO.Hidden.dynprompt],
|
||||
is_api_node=True,
|
||||
price_badge=p_series_price_badge(),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def execute(cls, model: dict) -> IO.NodeOutput:
|
||||
p_series_check_outputs(cls, model["quad"])
|
||||
views = {
|
||||
"front": model["image"],
|
||||
"left": model.get("image_left"),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from ._helpers import get_fs_object_size
|
||||
from ._helpers import get_fs_object_size, get_output_consumers, validate_output_unlinked
|
||||
from .client import (
|
||||
ApiEndpoint,
|
||||
poll_op,
|
||||
@@ -122,4 +122,7 @@ __all__ = [
|
||||
"validate_video_frame_count",
|
||||
# Misc functions
|
||||
"get_fs_object_size",
|
||||
# Graph helpers
|
||||
"get_output_consumers",
|
||||
"validate_output_unlinked",
|
||||
]
|
||||
|
||||
@@ -15,6 +15,7 @@ from comfy.comfy_api_env import normalize_comfy_api_base
|
||||
from comfy.deploy_environment import get_deploy_environment
|
||||
from comfy.model_management import processing_interrupted
|
||||
from comfy_api.latest import IO
|
||||
from comfy_execution.graph_utils import is_link
|
||||
from comfy_execution.utils import get_executing_context
|
||||
from comfyui_version import __version__ as comfyui_version
|
||||
|
||||
@@ -137,6 +138,27 @@ def get_fs_object_size(path_or_object: str | BytesIO) -> int:
|
||||
return len(path_or_object.getvalue())
|
||||
|
||||
|
||||
def get_output_consumers(node_cls: type[IO.ComfyNode], output_index: int) -> list[str]:
|
||||
dynprompt = node_cls.hidden.dynprompt
|
||||
if dynprompt is None:
|
||||
return []
|
||||
node_id = str(node_cls.hidden.unique_id)
|
||||
consumers = []
|
||||
for consumer_id in dynprompt.all_node_ids():
|
||||
consumer = dynprompt.get_node(consumer_id)
|
||||
for value in (consumer.get("inputs") or {}).values():
|
||||
if is_link(value) and value[0] == node_id and value[1] == output_index:
|
||||
title = (consumer.get("_meta") or {}).get("title") or consumer.get("class_type")
|
||||
consumers.append(f"{title} #{dynprompt.get_display_node_id(consumer_id)}")
|
||||
return sorted(consumers)
|
||||
|
||||
|
||||
def validate_output_unlinked(node_cls: type[IO.ComfyNode], output_index: int, reason: str) -> None:
|
||||
consumers = get_output_consumers(node_cls, output_index)
|
||||
if consumers:
|
||||
raise ValueError(f"{reason} (currently linked: {', '.join(consumers)}).")
|
||||
|
||||
|
||||
def to_aiohttp_url(url: str) -> URL:
|
||||
"""If `url` appears to be already percent-encoded (contains at least one valid %HH
|
||||
escape and no malformed '%' sequences) and contains no raw whitespace/control
|
||||
|
||||
Reference in New Issue
Block a user