diff --git a/video-gen-api/app/services/private_portrait/ark_client.py b/video-gen-api/app/services/private_portrait/ark_client.py index fe59f17e..8b6b3a35 100644 --- a/video-gen-api/app/services/private_portrait/ark_client.py +++ b/video-gen-api/app/services/private_portrait/ark_client.py @@ -4,6 +4,8 @@ import asyncio import json from typing import Any +from fastapi import HTTPException + from app.config import settings from app.enums.private_portrait import ( ARK_PRIVATE_PORTRAIT_HOST, @@ -135,13 +137,31 @@ class ArkPrivateAssetClient: credentials, 10, 60, + "https", ) - api_info = {action.value: ApiInfo("POST", "/", f"Action={action.value}&Version={ARK_PRIVATE_PORTRAIT_VERSION}", {}, {})} + # volcengine SDK 的 ApiInfo.query 必须是 dict,不能传 "Action=xxx&Version=xxx" 字符串。 + # ServiceInfo 默认 scheme='http',这里必须显式传 https,避免请求走 http://host:80。 + api_info = { + action.value: ApiInfo( + "POST", + "/", + {"Action": action.value, "Version": ARK_PRIVATE_PORTRAIT_VERSION}, + {}, + {}, + ) + } service = Service(service_info, api_info) + + # volcengine SDK 在签名阶段会对 request.body 做 hashlib.sha256(body)。 + # Python 3 下 hashlib.sha256 只能接收 bytes/bytearray,不能接收 dict 或 str。 + body = json.dumps(payload, ensure_ascii=False, separators=(",", ":")).encode("utf-8") try: - raw = service.json(action.value, {}, json.dumps(payload, ensure_ascii=False, separators=(",", ":"))) - except TypeError: - raw = service.json(action.value, {}, payload) + raw = service.json(action.value, {}, body) + except Exception as exc: + message = self._exception_message(exc) + if "ConnectTimeout" in message or "timed out" in message or "Connection" in message: + raise HTTPException(status_code=502, detail=f"火山私域素材接口连接失败:{message}") from exc + raise ArkPrivateAssetClientError(f"{action.value} 请求火山私域素材接口异常:{message}") from exc resp = self._normalize_response(raw) metadata = resp.get("ResponseMetadata") if isinstance(resp, dict) else None @@ -162,6 +182,15 @@ class ArkPrivateAssetClient: return resp return {"raw": resp, "RequestId": request_id} + @staticmethod + def _exception_message(exc: Exception) -> str: + if exc.args: + raw = exc.args[0] + if isinstance(raw, (bytes, bytearray)): + return raw.decode("utf-8", errors="ignore") + return str(raw) + return str(exc) + @staticmethod def _normalize_response(raw: Any) -> dict[str, Any]: if raw is None: