mirror of
https://git.datalinker.icu/comfyanonymous/ComfyUI
synced 2026-08-25 04:45:42 +08:00
converted tripo, veo2
This commit is contained in:
parent
c8f75cb322
commit
ceb1b7a6b3
@ -37,8 +37,8 @@ from comfy_api_nodes.apinode_utils import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def upload_image_to_tripo(image, **kwargs):
|
async def upload_image_to_tripo(image, **kwargs):
|
||||||
urls = upload_images_to_comfyapi(image, max_images=1, auth_kwargs=kwargs)
|
urls = await upload_images_to_comfyapi(image, max_images=1, auth_kwargs=kwargs)
|
||||||
return TripoFileReference(TripoUrlReference(url=urls[0], type="jpeg"))
|
return TripoFileReference(TripoUrlReference(url=urls[0], type="jpeg"))
|
||||||
|
|
||||||
def get_model_url_from_response(response: TripoTaskResponse) -> str:
|
def get_model_url_from_response(response: TripoTaskResponse) -> str:
|
||||||
@ -49,7 +49,7 @@ def get_model_url_from_response(response: TripoTaskResponse) -> str:
|
|||||||
raise RuntimeError(f"Failed to get model url from response: {response}")
|
raise RuntimeError(f"Failed to get model url from response: {response}")
|
||||||
|
|
||||||
|
|
||||||
def poll_until_finished(
|
async def poll_until_finished(
|
||||||
kwargs: dict[str, str],
|
kwargs: dict[str, str],
|
||||||
response: TripoTaskResponse,
|
response: TripoTaskResponse,
|
||||||
) -> tuple[str, str]:
|
) -> tuple[str, str]:
|
||||||
@ -57,7 +57,7 @@ def poll_until_finished(
|
|||||||
if response.code != 0:
|
if response.code != 0:
|
||||||
raise RuntimeError(f"Failed to generate mesh: {response.error}")
|
raise RuntimeError(f"Failed to generate mesh: {response.error}")
|
||||||
task_id = response.data.task_id
|
task_id = response.data.task_id
|
||||||
response_poll = PollingOperation(
|
response_poll = await PollingOperation(
|
||||||
poll_endpoint=ApiEndpoint(
|
poll_endpoint=ApiEndpoint(
|
||||||
path=f"/proxy/tripo/v2/openapi/task/{task_id}",
|
path=f"/proxy/tripo/v2/openapi/task/{task_id}",
|
||||||
method=HttpMethod.GET,
|
method=HttpMethod.GET,
|
||||||
@ -80,7 +80,7 @@ def poll_until_finished(
|
|||||||
).execute()
|
).execute()
|
||||||
if response_poll.data.status == TripoTaskStatus.SUCCESS:
|
if response_poll.data.status == TripoTaskStatus.SUCCESS:
|
||||||
url = get_model_url_from_response(response_poll)
|
url = get_model_url_from_response(response_poll)
|
||||||
bytesio = download_url_to_bytesio(url)
|
bytesio = await download_url_to_bytesio(url)
|
||||||
# Save the downloaded model file
|
# Save the downloaded model file
|
||||||
model_file = f"tripo_model_{task_id}.glb"
|
model_file = f"tripo_model_{task_id}.glb"
|
||||||
with open(os.path.join(get_output_directory(), model_file), "wb") as f:
|
with open(os.path.join(get_output_directory(), model_file), "wb") as f:
|
||||||
@ -88,6 +88,7 @@ def poll_until_finished(
|
|||||||
return model_file, task_id
|
return model_file, task_id
|
||||||
raise RuntimeError(f"Failed to generate mesh: {response_poll}")
|
raise RuntimeError(f"Failed to generate mesh: {response_poll}")
|
||||||
|
|
||||||
|
|
||||||
class TripoTextToModelNode:
|
class TripoTextToModelNode:
|
||||||
"""
|
"""
|
||||||
Generates 3D models synchronously based on a text prompt using Tripo's API.
|
Generates 3D models synchronously based on a text prompt using Tripo's API.
|
||||||
@ -126,11 +127,11 @@ class TripoTextToModelNode:
|
|||||||
API_NODE = True
|
API_NODE = True
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
def generate_mesh(self, prompt, negative_prompt=None, model_version=None, style=None, texture=None, pbr=None, image_seed=None, model_seed=None, texture_seed=None, texture_quality=None, face_limit=None, quad=None, **kwargs):
|
async def generate_mesh(self, prompt, negative_prompt=None, model_version=None, style=None, texture=None, pbr=None, image_seed=None, model_seed=None, texture_seed=None, texture_quality=None, face_limit=None, quad=None, **kwargs):
|
||||||
style_enum = None if style == "None" else style
|
style_enum = None if style == "None" else style
|
||||||
if not prompt:
|
if not prompt:
|
||||||
raise RuntimeError("Prompt is required")
|
raise RuntimeError("Prompt is required")
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -155,7 +156,8 @@ class TripoTextToModelNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoImageToModelNode:
|
class TripoImageToModelNode:
|
||||||
"""
|
"""
|
||||||
@ -195,12 +197,12 @@ class TripoImageToModelNode:
|
|||||||
API_NODE = True
|
API_NODE = True
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
def generate_mesh(self, image, model_version=None, style=None, texture=None, pbr=None, model_seed=None, orientation=None, texture_alignment=None, texture_seed=None, texture_quality=None, face_limit=None, quad=None, **kwargs):
|
async def generate_mesh(self, image, model_version=None, style=None, texture=None, pbr=None, model_seed=None, orientation=None, texture_alignment=None, texture_seed=None, texture_quality=None, face_limit=None, quad=None, **kwargs):
|
||||||
style_enum = None if style == "None" else style
|
style_enum = None if style == "None" else style
|
||||||
if image is None:
|
if image is None:
|
||||||
raise RuntimeError("Image is required")
|
raise RuntimeError("Image is required")
|
||||||
tripo_file = upload_image_to_tripo(image, **kwargs)
|
tripo_file = await upload_image_to_tripo(image, **kwargs)
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -225,7 +227,8 @@ class TripoImageToModelNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoMultiviewToModelNode:
|
class TripoMultiviewToModelNode:
|
||||||
"""
|
"""
|
||||||
@ -267,7 +270,7 @@ class TripoMultiviewToModelNode:
|
|||||||
API_NODE = True
|
API_NODE = True
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
def generate_mesh(self, image, image_left=None, image_back=None, image_right=None, model_version=None, orientation=None, texture=None, pbr=None, model_seed=None, texture_seed=None, texture_quality=None, texture_alignment=None, face_limit=None, quad=None, **kwargs):
|
async def generate_mesh(self, image, image_left=None, image_back=None, image_right=None, model_version=None, orientation=None, texture=None, pbr=None, model_seed=None, texture_seed=None, texture_quality=None, texture_alignment=None, face_limit=None, quad=None, **kwargs):
|
||||||
if image is None:
|
if image is None:
|
||||||
raise RuntimeError("front image for multiview is required")
|
raise RuntimeError("front image for multiview is required")
|
||||||
images = []
|
images = []
|
||||||
@ -282,11 +285,11 @@ class TripoMultiviewToModelNode:
|
|||||||
for image_name in ["image", "image_left", "image_back", "image_right"]:
|
for image_name in ["image", "image_left", "image_back", "image_right"]:
|
||||||
image_ = image_dict[image_name]
|
image_ = image_dict[image_name]
|
||||||
if image_ is not None:
|
if image_ is not None:
|
||||||
tripo_file = upload_image_to_tripo(image_, **kwargs)
|
tripo_file = await upload_image_to_tripo(image_, **kwargs)
|
||||||
images.append(tripo_file)
|
images.append(tripo_file)
|
||||||
else:
|
else:
|
||||||
images.append(TripoFileEmptyReference())
|
images.append(TripoFileEmptyReference())
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -309,7 +312,8 @@ class TripoMultiviewToModelNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoTextureNode:
|
class TripoTextureNode:
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -340,8 +344,8 @@ class TripoTextureNode:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
AVERAGE_DURATION = 80
|
AVERAGE_DURATION = 80
|
||||||
|
|
||||||
def generate_mesh(self, model_task_id, texture=None, pbr=None, texture_seed=None, texture_quality=None, texture_alignment=None, **kwargs):
|
async def generate_mesh(self, model_task_id, texture=None, pbr=None, texture_seed=None, texture_quality=None, texture_alignment=None, **kwargs):
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -358,7 +362,7 @@ class TripoTextureNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoRefineNode:
|
class TripoRefineNode:
|
||||||
@ -387,8 +391,8 @@ class TripoRefineNode:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
AVERAGE_DURATION = 240
|
AVERAGE_DURATION = 240
|
||||||
|
|
||||||
def generate_mesh(self, model_task_id, **kwargs):
|
async def generate_mesh(self, model_task_id, **kwargs):
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -400,7 +404,7 @@ class TripoRefineNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoRigNode:
|
class TripoRigNode:
|
||||||
@ -425,8 +429,8 @@ class TripoRigNode:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
AVERAGE_DURATION = 180
|
AVERAGE_DURATION = 180
|
||||||
|
|
||||||
def generate_mesh(self, original_model_task_id, **kwargs):
|
async def generate_mesh(self, original_model_task_id, **kwargs):
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -440,7 +444,8 @@ class TripoRigNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoRetargetNode:
|
class TripoRetargetNode:
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -475,8 +480,8 @@ class TripoRetargetNode:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
AVERAGE_DURATION = 30
|
AVERAGE_DURATION = 30
|
||||||
|
|
||||||
def generate_mesh(self, animation, original_model_task_id, **kwargs):
|
async def generate_mesh(self, animation, original_model_task_id, **kwargs):
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -491,7 +496,8 @@ class TripoRetargetNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
class TripoConversionNode:
|
class TripoConversionNode:
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -529,10 +535,10 @@ class TripoConversionNode:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
AVERAGE_DURATION = 30
|
AVERAGE_DURATION = 30
|
||||||
|
|
||||||
def generate_mesh(self, original_model_task_id, format, quad, face_limit, texture_size, texture_format, **kwargs):
|
async def generate_mesh(self, original_model_task_id, format, quad, face_limit, texture_size, texture_format, **kwargs):
|
||||||
if not original_model_task_id:
|
if not original_model_task_id:
|
||||||
raise RuntimeError("original_model_task_id is required")
|
raise RuntimeError("original_model_task_id is required")
|
||||||
response = SynchronousOperation(
|
response = await SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/tripo/v2/openapi/task",
|
path="/proxy/tripo/v2/openapi/task",
|
||||||
method=HttpMethod.POST,
|
method=HttpMethod.POST,
|
||||||
@ -549,7 +555,8 @@ class TripoConversionNode:
|
|||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
).execute()
|
).execute()
|
||||||
return poll_until_finished(kwargs, response)
|
return await poll_until_finished(kwargs, response)
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"TripoTextToModelNode": TripoTextToModelNode,
|
"TripoTextToModelNode": TripoTextToModelNode,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
import requests
|
import aiohttp
|
||||||
import torch
|
import torch
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ class VeoVideoGenerationNode(ComfyNodeABC):
|
|||||||
DESCRIPTION = "Generates videos from text prompts using Google's Veo API"
|
DESCRIPTION = "Generates videos from text prompts using Google's Veo API"
|
||||||
API_NODE = True
|
API_NODE = True
|
||||||
|
|
||||||
def generate_video(
|
async def generate_video(
|
||||||
self,
|
self,
|
||||||
prompt,
|
prompt,
|
||||||
aspect_ratio="16:9",
|
aspect_ratio="16:9",
|
||||||
@ -204,7 +204,7 @@ class VeoVideoGenerationNode(ComfyNodeABC):
|
|||||||
auth_kwargs=kwargs,
|
auth_kwargs=kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
initial_response = initial_operation.execute()
|
initial_response = await initial_operation.execute()
|
||||||
operation_name = initial_response.name
|
operation_name = initial_response.name
|
||||||
|
|
||||||
logging.info(f"Veo generation started with operation name: {operation_name}")
|
logging.info(f"Veo generation started with operation name: {operation_name}")
|
||||||
@ -243,7 +243,7 @@ class VeoVideoGenerationNode(ComfyNodeABC):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Execute the polling operation
|
# Execute the polling operation
|
||||||
poll_response = poll_operation.execute()
|
poll_response = await poll_operation.execute()
|
||||||
|
|
||||||
# Now check for errors in the final response
|
# Now check for errors in the final response
|
||||||
# Check for error in poll response
|
# Check for error in poll response
|
||||||
@ -268,7 +268,6 @@ class VeoVideoGenerationNode(ComfyNodeABC):
|
|||||||
raise Exception(error_message)
|
raise Exception(error_message)
|
||||||
|
|
||||||
# Extract video data
|
# Extract video data
|
||||||
video_data = None
|
|
||||||
if poll_response.response and hasattr(poll_response.response, 'videos') and poll_response.response.videos and len(poll_response.response.videos) > 0:
|
if poll_response.response and hasattr(poll_response.response, 'videos') and poll_response.response.videos and len(poll_response.response.videos) > 0:
|
||||||
video = poll_response.response.videos[0]
|
video = poll_response.response.videos[0]
|
||||||
|
|
||||||
@ -278,9 +277,9 @@ class VeoVideoGenerationNode(ComfyNodeABC):
|
|||||||
video_data = base64.b64decode(video.bytesBase64Encoded)
|
video_data = base64.b64decode(video.bytesBase64Encoded)
|
||||||
elif hasattr(video, 'gcsUri') and video.gcsUri:
|
elif hasattr(video, 'gcsUri') and video.gcsUri:
|
||||||
# Download from URL
|
# Download from URL
|
||||||
video_url = video.gcsUri
|
async with aiohttp.ClientSession() as session:
|
||||||
video_response = requests.get(video_url)
|
async with session.get(video.gcsUri) as video_response:
|
||||||
video_data = video_response.content
|
video_data = await video_response.content.read()
|
||||||
else:
|
else:
|
||||||
raise Exception("Video returned but no data or URL was provided")
|
raise Exception("Video returned but no data or URL was provided")
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user