""" Seedance 素材节点 节点列表: - SeedanceElementCreate: 创建图片、视频或音频素材 """ import json from ..clients.seedance_element_client import SeedanceElementClient from ..utils.image_utils import tensor_to_pil from ..utils.r2_uploader import upload_audio, upload_image, upload_video from ..utils.config import get_base_url_by_route from ..utils.seedance_assets import SeedanceAssetService class SeedanceElementCreate: """Seedance 创建素材""" @classmethod def INPUT_TYPES(cls): return { "required": { "素材名称": ("STRING", {"default": ""}), "请求模式": (["HC", "Doubao"], {"default": "HC"}), }, "optional": { "照片": ("IMAGE",), "视频": ("VIDEO",), "音频": ("AUDIO",), "素材描述": ("STRING", {"multiline": True, "default": ""}), }, } RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("查询信息", "提取ID") FUNCTION = "create_element" CATEGORY = "comfyui_o1key/Seedance" async def create_element(self, **kwargs): name = kwargs["素材名称"].strip() request_mode = kwargs.get("请求模式", "HC") # 旧名称保留为执行期别名,兼容未经过前端迁移的 API 工作流。 image_tensor = kwargs.get("照片") video = kwargs.get("视频") audio = kwargs.get("音频") if image_tensor is None: image_tensor = kwargs.get("真人照片") if video is None: video = kwargs.get("真人视频") if audio is None: audio = kwargs.get("真人音频") if request_mode in {"标准", "高并发"}: request_mode = "HC" request_types = {"HC": "hc", "Doubao": "doubao"} if request_mode not in request_types: raise ValueError(f"不支持的请求模式:{request_mode}") request_type = request_types[request_mode] base_url = get_base_url_by_route() sources = [] if image_tensor is not None: sources.append(("Image", image_tensor)) if video is not None: sources.append(("Video", video)) if audio is not None: sources.append(("Audio", audio)) if len(sources) != 1: raise ValueError( f"{request_mode} 模式必须在照片、视频、音频中恰好提供一种素材" ) asset_type, source = sources[0] if asset_type == "Image": pil_images = tensor_to_pil(source) if not pil_images: raise ValueError("无法读取图片") pil_image = pil_images[0] if pil_image.mode == "RGBA": pil_image = pil_image.convert("RGB") print(f"[Seedance素材][{request_mode}] 上传图片中...") asset_url = await upload_image(pil_image, base_url=base_url) elif asset_type == "Video": print(f"[Seedance素材][{request_mode}] 上传视频中...") asset_url = await upload_video(source, base_url=base_url) else: print(f"[Seedance素材][{request_mode}] 上传音频中...") asset_url = await upload_audio(source, base_url=base_url) if not asset_url.startswith("https://"): raise ValueError(f"{request_mode} 素材上传后未获得 HTTPS 公网地址") print( f"[Seedance素材][{request_mode}] 创建素材: " f"Name={name or '(空)'}, AssetType={asset_type}, URL=<临时地址已折叠>" ) service = SeedanceAssetService( client=SeedanceElementClient(base_url=base_url), ) result = await service.create_from_url( name=name, asset_url=asset_url, asset_type=asset_type, request_type=request_type, ) element_id = result.get("Id") if not element_id: raise RuntimeError(f"{request_mode} 素材已激活但未返回 Id,响应:{result}") create_response = result.get("_create_response", {}) response_json = json.dumps(create_response, ensure_ascii=False, indent=2) print(f"[Seedance素材] 创建成功 → {element_id}") return (response_json, element_id) # ── 节点注册 ────────────────────────────────────────────────────────────────── NODE_CLASS_MAPPINGS = { "SeedanceElementCreate": SeedanceElementCreate, } NODE_DISPLAY_NAME_MAPPINGS = { "SeedanceElementCreate": "Seedance 创建素材", }