Skip to content

Commit c86ff0a

Browse files
committed
fix(api-nodes): added "texture_image" output to TencentTextToModel and TencentImageToModel nodes. Fixed OBJ output when it is zipped
1 parent 0a7f8e1 commit c86ff0a

1 file changed

Lines changed: 40 additions & 6 deletions

File tree

comfy_api_nodes/nodes_hunyuan3d.py

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import zipfile
2+
from io import BytesIO
3+
14
from typing_extensions import override
25

36
from comfy_api.latest import IO, ComfyExtension, Input, Types
@@ -15,6 +18,8 @@
1518
)
1619
from comfy_api_nodes.util import (
1720
ApiEndpoint,
21+
bytesio_to_image_tensor,
22+
download_url_to_bytesio,
1823
download_url_to_file_3d,
1924
download_url_to_image_tensor,
2025
downscale_image_tensor_by_max_side,
@@ -35,6 +40,29 @@ def _is_tencent_rate_limited(status: int, body: object) -> bool:
3540
)
3641

3742

43+
async def download_and_extract_obj_zip(url: str) -> tuple[Types.File3D, Input.Image | None]:
44+
"""The Tencent API returns OBJ results as ZIP archives containing the .obj mesh, and a texture image."""
45+
data = BytesIO()
46+
await download_url_to_bytesio(url, data)
47+
data.seek(0)
48+
if not zipfile.is_zipfile(data):
49+
data.seek(0)
50+
return Types.File3D(source=data, file_format="obj"), None
51+
data.seek(0)
52+
obj_bytes = None
53+
texture_tensor = None
54+
with zipfile.ZipFile(data) as zf:
55+
for name in zf.namelist():
56+
lower = name.lower()
57+
if lower.endswith(".obj"):
58+
obj_bytes = zf.read(name)
59+
elif any(lower.endswith(ext) for ext in (".png", ".jpg", ".jpeg", ".bmp", ".tiff", ".webp")):
60+
texture_tensor = bytesio_to_image_tensor(BytesIO(zf.read(name)), mode="RGB")
61+
if obj_bytes is None:
62+
raise ValueError("ZIP archive does not contain an OBJ file.")
63+
return Types.File3D(source=BytesIO(obj_bytes), file_format="obj"), texture_tensor
64+
65+
3866
def get_file_from_response(
3967
response_objs: list[ResultFile3D], file_type: str, raise_if_not_found: bool = True
4068
) -> ResultFile3D | None:
@@ -92,6 +120,7 @@ def define_schema(cls):
92120
IO.String.Output(display_name="model_file"), # for backward compatibility only
93121
IO.File3DGLB.Output(display_name="GLB"),
94122
IO.File3DOBJ.Output(display_name="OBJ"),
123+
IO.Image.Output(display_name="texture_image"),
95124
],
96125
hidden=[
97126
IO.Hidden.auth_token_comfy_org,
@@ -150,14 +179,16 @@ async def execute(
150179
response_model=To3DProTaskResultResponse,
151180
status_extractor=lambda r: r.Status,
152181
)
182+
obj_file, texture_image = await download_and_extract_obj_zip(
183+
get_file_from_response(result.ResultFile3Ds, "obj").Url
184+
)
153185
return IO.NodeOutput(
154186
f"{task_id}.glb",
155187
await download_url_to_file_3d(
156188
get_file_from_response(result.ResultFile3Ds, "glb").Url, "glb", task_id=task_id
157189
),
158-
await download_url_to_file_3d(
159-
get_file_from_response(result.ResultFile3Ds, "obj").Url, "obj", task_id=task_id
160-
),
190+
obj_file,
191+
texture_image,
161192
)
162193

163194

@@ -210,6 +241,7 @@ def define_schema(cls):
210241
IO.String.Output(display_name="model_file"), # for backward compatibility only
211242
IO.File3DGLB.Output(display_name="GLB"),
212243
IO.File3DOBJ.Output(display_name="OBJ"),
244+
IO.Image.Output(display_name="texture_image"),
213245
],
214246
hidden=[
215247
IO.Hidden.auth_token_comfy_org,
@@ -303,14 +335,16 @@ async def execute(
303335
response_model=To3DProTaskResultResponse,
304336
status_extractor=lambda r: r.Status,
305337
)
338+
obj_file, texture_image = await download_and_extract_obj_zip(
339+
get_file_from_response(result.ResultFile3Ds, "obj").Url
340+
)
306341
return IO.NodeOutput(
307342
f"{task_id}.glb",
308343
await download_url_to_file_3d(
309344
get_file_from_response(result.ResultFile3Ds, "glb").Url, "glb", task_id=task_id
310345
),
311-
await download_url_to_file_3d(
312-
get_file_from_response(result.ResultFile3Ds, "obj").Url, "obj", task_id=task_id
313-
),
346+
obj_file,
347+
texture_image,
314348
)
315349

316350

0 commit comments

Comments
 (0)