Compare commits

..

1 Commits

Author SHA1 Message Date
7ee77ff038 Add name to LoraLoaderModelOnly. (#12078) 2026-01-25 21:01:55 -05:00
10 changed files with 13 additions and 155 deletions

View File

@ -7,7 +7,7 @@ from comfy_api.internal.singleton import ProxiedSingleton
from comfy_api.internal.async_to_sync import create_sync_class
from ._input import ImageInput, AudioInput, MaskInput, LatentInput, VideoInput
from ._input_impl import VideoFromFile, VideoFromComponents
from ._util import VideoCodec, VideoContainer, VideoComponents, MESH, VOXEL, File3D
from ._util import VideoCodec, VideoContainer, VideoComponents, MESH, VOXEL
from . import _io_public as io
from . import _ui_public as ui
from comfy_execution.utils import get_executing_context
@ -105,7 +105,6 @@ class Types:
VideoComponents = VideoComponents
MESH = MESH
VOXEL = VOXEL
File3D = File3D
ComfyAPI = ComfyAPI_latest

View File

@ -27,7 +27,7 @@ if TYPE_CHECKING:
from comfy_api.internal import (_ComfyNodeInternal, _NodeOutputInternal, classproperty, copy_class, first_real_override, is_class,
prune_dict, shallow_clone_class)
from comfy_execution.graph_utils import ExecutionBlocker
from ._util import MESH, VOXEL, File3D, SVG as _SVG
from ._util import MESH, VOXEL, SVG as _SVG
class FolderType(str, Enum):
@ -667,49 +667,6 @@ class Voxel(ComfyTypeIO):
class Mesh(ComfyTypeIO):
Type = MESH
@comfytype(io_type="FILE_3D_GLB")
class File3DGLB(ComfyTypeIO):
"""GLB format 3D file - binary glTF, best for web and cross-platform."""
Type = File3D
@comfytype(io_type="FILE_3D_GLTF")
class File3DGLTF(ComfyTypeIO):
"""GLTF format 3D file - JSON-based glTF with external resources."""
Type = File3D
@comfytype(io_type="FILE_3D_FBX")
class File3DFBX(ComfyTypeIO):
"""FBX format 3D file - best for game engines and animation."""
Type = File3D
@comfytype(io_type="FILE_3D_OBJ")
class File3DOBJ(ComfyTypeIO):
"""OBJ format 3D file - simple geometry format."""
Type = File3D
@comfytype(io_type="FILE_3D_STL")
class File3DSTL(ComfyTypeIO):
"""STL format 3D file - best for 3D printing."""
Type = File3D
@comfytype(io_type="FILE_3D_PLY")
class File3DPLY(ComfyTypeIO):
"""PLY format 3D file - polygon file format, supports point clouds."""
Type = File3D
@comfytype(io_type="FILE_3D_USDZ")
class File3DUSDZ(ComfyTypeIO):
"""USDZ format 3D file - Apple AR format."""
Type = File3D
@comfytype(io_type="HOOKS")
class Hooks(ComfyTypeIO):
if TYPE_CHECKING:
@ -2109,13 +2066,6 @@ __all__ = [
"LossMap",
"Voxel",
"Mesh",
"File3DGLB",
"File3DGLTF",
"File3DFBX",
"File3DOBJ",
"File3DSTL",
"File3DPLY",
"File3DUSDZ",
"Hooks",
"HookKeyframes",
"TimestepsRange",

View File

@ -1,5 +1,5 @@
from .video_types import VideoContainer, VideoCodec, VideoComponents
from .geometry_types import VOXEL, MESH, File3D
from .geometry_types import VOXEL, MESH
from .image_types import SVG
__all__ = [
@ -9,6 +9,5 @@ __all__ = [
"VideoComponents",
"VOXEL",
"MESH",
"File3D",
"SVG",
]

View File

@ -1,5 +1,3 @@
from pathlib import Path
import torch
@ -12,35 +10,3 @@ class MESH:
def __init__(self, vertices: torch.Tensor, faces: torch.Tensor):
self.vertices = vertices
self.faces = faces
class File3D:
"""Basic 3D file type representing a file path with format information.
This is the backing class for all FILE_3D_* ComfyTypes.
"""
def __init__(
self,
file_path: str | Path,
file_format: str | None = None,
):
self.file_path = str(file_path)
self.format = file_format or self._detect_format(self.file_path)
@staticmethod
def _detect_format(file_path: str | Path) -> str:
"""Detect format from file extension."""
ext = file_path.lower().rsplit(".", 1)[-1] if "." in file_path else ""
return {
"glb": "glb",
"gltf": "gltf",
"fbx": "fbx",
"obj": "obj",
"stl": "stl",
"usdz": "usdz",
"ply": "ply",
}.get(ext, "glb")
def __repr__(self) -> str:
return f"File3D({self.format}: {self.file_path})"

View File

@ -109,9 +109,6 @@ class MeshyTextureRequest(BaseModel):
class MeshyModelsUrls(BaseModel):
glb: str = Field("")
fbx: str = Field("")
usdz: str = Field("")
obj: str = Field("")
class MeshyRiggedModelsUrls(BaseModel):

View File

@ -1,3 +1,5 @@
import os
from typing_extensions import override
from comfy_api.latest import IO, ComfyExtension, Input
@ -18,7 +20,6 @@ from comfy_api_nodes.apis.meshy import (
)
from comfy_api_nodes.util import (
ApiEndpoint,
download_url_to_file_3d,
download_url_to_bytesio,
poll_op,
sync_op,
@ -26,7 +27,6 @@ from comfy_api_nodes.util import (
validate_string,
)
from folder_paths import get_output_directory
import os
class MeshyTextToModelNode(IO.ComfyNode):
@ -81,10 +81,6 @@ class MeshyTextToModelNode(IO.ComfyNode):
outputs=[
IO.String.Output(display_name="model_file"),
IO.Custom("MESHY_TASK_ID").Output(display_name="meshy_task_id"),
IO.File3DGLB.Output(display_name="GLB"),
IO.File3DFBX.Output(display_name="FBX"),
IO.File3DOBJ.Output(display_name="OBJ"),
IO.File3DUSDZ.Output(display_name="USDZ"),
],
hidden=[
IO.Hidden.auth_token_comfy_org,
@ -135,18 +131,7 @@ class MeshyTextToModelNode(IO.ComfyNode):
)
model_file = f"meshy_model_{response.result}.glb"
await download_url_to_bytesio(result.model_urls.glb, os.path.join(get_output_directory(), model_file))
return IO.NodeOutput(model_file, response.result, model_file, None, None, None)
task_id = response.result
m_urls = result.model_urls
file_glb = await download_url_to_file_3d(m_urls.glb, task_id, "glb") if m_urls.glb else None
return IO.NodeOutput(
file_glb.file_path if file_glb else "",
task_id,
file_glb,
await download_url_to_file_3d(m_urls.fbx, task_id, "fbx") if m_urls.fbx else None,
await download_url_to_file_3d(m_urls.obj, task_id, "obj") if m_urls.obj else None,
await download_url_to_file_3d(m_urls.usdz, task_id, "usdz") if m_urls.usdz else None,
)
return IO.NodeOutput(model_file, response.result)
class MeshyRefineNode(IO.ComfyNode):

View File

@ -28,7 +28,6 @@ from .conversions import (
from .download_helpers import (
download_url_as_bytesio,
download_url_to_bytesio,
download_url_to_file_3d,
download_url_to_image_tensor,
download_url_to_video_output,
)
@ -70,7 +69,6 @@ __all__ = [
# Download helpers
"download_url_as_bytesio",
"download_url_to_bytesio",
"download_url_to_file_3d",
"download_url_to_image_tensor",
"download_url_to_video_output",
# Conversions

View File

@ -11,8 +11,7 @@ import torch
from aiohttp.client_exceptions import ClientError, ContentTypeError
from comfy_api.latest import IO as COMFY_IO
from comfy_api.latest import InputImpl, Types
from folder_paths import get_output_directory
from comfy_api.latest import InputImpl
from . import request_logger
from ._helpers import (
@ -262,25 +261,3 @@ def _generate_operation_id(method: str, url: str, attempt: int) -> str:
except Exception:
slug = "download"
return f"{method}_{slug}_try{attempt}_{uuid.uuid4().hex[:8]}"
async def download_url_to_file_3d(
url: str,
task_id: str,
file_format: str,
*,
timeout: float | None = None,
max_retries: int = 5,
cls: type[COMFY_IO.ComfyNode] = None,
) -> Types.File3D:
"""Downloads a 3D model file from a URL and saves it to output/{task_id}.{format}."""
file_format = file_format.lstrip(".").lower()
file_name = f"{task_id}.{file_format}"
await download_url_to_bytesio(
url,
Path(get_output_directory()).joinpath(file_name),
timeout=timeout,
max_retries=max_retries,
cls=cls,
)
return Types.File3D(file_path=file_name, file_format=file_format)

View File

@ -3,7 +3,7 @@ import folder_paths
import os
from typing_extensions import override
from comfy_api.latest import IO, UI, ComfyExtension, InputImpl, Types
from comfy_api.latest import IO, ComfyExtension, InputImpl, UI
from pathlib import Path
@ -81,20 +81,7 @@ class Preview3D(IO.ComfyNode):
is_experimental=True,
is_output_node=True,
inputs=[
IO.MultiType.Input(
"model_file",
types=[
IO.File3DGLB,
IO.File3DGLTF,
IO.File3DFBX,
IO.File3DOBJ,
IO.File3DSTL,
IO.File3DPLY,
IO.File3DUSDZ,
IO.String, # backward compatibility
],
tooltip="3D model file or path string",
),
IO.String.Input("model_file", default="", multiline=False),
IO.Load3DCamera.Input("camera_info", optional=True),
IO.Image.Input("bg_image", optional=True),
],
@ -102,11 +89,10 @@ class Preview3D(IO.ComfyNode):
)
@classmethod
def execute(cls, model_file: str | Types.File3D, **kwargs) -> IO.NodeOutput:
model_path = model_file.file_path if isinstance(model_file, Types.File3D) else model_file
def execute(cls, model_file, **kwargs) -> IO.NodeOutput:
camera_info = kwargs.get("camera_info", None)
bg_image = kwargs.get("bg_image", None)
return IO.NodeOutput(ui=UI.PreviewUI3D(model_path, camera_info, bg_image=bg_image))
return IO.NodeOutput(ui=UI.PreviewUI3D(model_file, camera_info, bg_image=bg_image))
process = execute # TODO: remove

View File

@ -2105,7 +2105,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
"CheckpointLoader": "Load Checkpoint With Config (DEPRECATED)",
"CheckpointLoaderSimple": "Load Checkpoint",
"VAELoader": "Load VAE",
"LoraLoader": "Load LoRA",
"LoraLoader": "Load LoRA (Model and CLIP)",
"LoraLoaderModelOnly": "Load LoRA",
"CLIPLoader": "Load CLIP",
"ControlNetLoader": "Load ControlNet Model",
"DiffControlNetLoader": "Load ControlNet Model (diff)",