mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-23 23:53:55 +08:00
feat: add FastAPI manager API compatibility baseline
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class VoiceCloneProviderError(Exception):
|
||||
code: int
|
||||
message: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.message
|
||||
|
||||
|
||||
class VoiceCloneIntegration:
|
||||
ENDPOINT = "https://openspeech.bytedance.com/api/v1/mega_tts/audio/upload"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
timeout_seconds: float,
|
||||
client: httpx.AsyncClient | None = None,
|
||||
endpoint: str | None = None,
|
||||
):
|
||||
self.timeout_seconds = timeout_seconds
|
||||
self.client = client
|
||||
self.endpoint = endpoint or self.ENDPOINT
|
||||
|
||||
async def train_huoshan(
|
||||
self,
|
||||
*,
|
||||
appid: str,
|
||||
access_token: str,
|
||||
voice: bytes,
|
||||
speaker_id: str,
|
||||
) -> str:
|
||||
request_body: dict[str, Any] = {
|
||||
"appid": appid,
|
||||
"audios": [
|
||||
{
|
||||
"audio_bytes": base64.b64encode(voice).decode("ascii"),
|
||||
"audio_format": "wav",
|
||||
}
|
||||
],
|
||||
"source": 2,
|
||||
"language": 0,
|
||||
"model_type": 1,
|
||||
"speaker_id": speaker_id,
|
||||
}
|
||||
owns_client = self.client is None
|
||||
client = self.client or httpx.AsyncClient()
|
||||
try:
|
||||
response = await client.post(
|
||||
self.endpoint,
|
||||
content=json.dumps(request_body, ensure_ascii=False, separators=(",", ":")).encode("utf-8"),
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer;{access_token}",
|
||||
"Resource-Id": "seed-icl-1.0",
|
||||
},
|
||||
timeout=self.timeout_seconds,
|
||||
)
|
||||
try:
|
||||
payload = response.json()
|
||||
except (json.JSONDecodeError, ValueError) as exc:
|
||||
raise VoiceCloneProviderError(10157, str(exc)) from exc
|
||||
except httpx.HTTPError as exc:
|
||||
raise VoiceCloneProviderError(10157, str(exc)) from exc
|
||||
finally:
|
||||
if owns_client:
|
||||
await client.aclose()
|
||||
|
||||
if not isinstance(payload, dict):
|
||||
raise VoiceCloneProviderError(10156, "响应格式错误,缺少BaseResp字段")
|
||||
base_response = payload.get("BaseResp")
|
||||
if isinstance(base_response, dict):
|
||||
raw_status = base_response.get("StatusCode")
|
||||
try:
|
||||
status_code = int(raw_status) if raw_status is not None else None
|
||||
except (TypeError, ValueError):
|
||||
status_code = None
|
||||
returned_speaker = payload.get("speaker_id")
|
||||
if status_code == 0 and isinstance(returned_speaker, str) and returned_speaker.strip():
|
||||
return returned_speaker
|
||||
status_message = base_response.get("StatusMessage")
|
||||
message = str(status_message) if status_message not in (None, "") else "训练失败"
|
||||
raise VoiceCloneProviderError(500, message)
|
||||
payload_message = payload.get("message")
|
||||
if payload_message not in (None, ""):
|
||||
raise VoiceCloneProviderError(500, str(payload_message))
|
||||
raise VoiceCloneProviderError(10156, "响应格式错误,缺少BaseResp字段")
|
||||
Reference in New Issue
Block a user