FishClient.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. from io import BytesIO
  2. from typing import Dict, Any, List
  3. import requests
  4. class FishClient(object):
  5. def __init__(self, base_url: str):
  6. self.base_url = base_url
  7. def get_all_references_id(self) -> List[str]:
  8. """
  9. 获取已存在的参考音频
  10. Returns:
  11. 参考音频ID列表
  12. Raises:
  13. requests.RequestException: 下载或上传请求失败
  14. ValueError: 服务端返回错误响应或下载内容为空
  15. """
  16. url = f"{self.base_url}/v1/references/list"
  17. headers = self.build_common_header()
  18. response = requests.get(url, headers=headers)
  19. return response.json().get("reference_ids", [])
  20. def add_reference_id_by_url(self, reference_id: str, reference_text: str, audio_url: str):
  21. """
  22. 从 URL 下载音频并调用添加参考音频的接口
  23. Args:
  24. reference_id: 参考音频唯一标识
  25. audio_url: 音频文件的 URL(支持 http/https)
  26. reference_text: 音频对应的文本内容
  27. Raises:
  28. requests.RequestException: 下载或上传请求失败
  29. ValueError: 服务端返回错误响应或下载内容为空
  30. """
  31. try:
  32. resp = requests.get(audio_url, timeout=30)
  33. resp.raise_for_status() # 检查 HTTP 错误
  34. audio_content = resp.content
  35. if not audio_content:
  36. raise ValueError("从 URL 下载的音频文件为空")
  37. except requests.exceptions.RequestException as e:
  38. raise requests.RequestException(f"下载音频失败: {e}") from e
  39. url = f"{self.base_url}/v1/references/add"
  40. payload = {
  41. "id": reference_id,
  42. "text": reference_text,
  43. }
  44. # 定义文件名
  45. file_name = audio_url.split('/')[-1] or "audio.wav"
  46. # 使用 BytesIO 包装音频内容
  47. files = {
  48. "audio": (file_name, BytesIO(audio_content), "audio/wav"),
  49. }
  50. headers = self.build_common_header()
  51. try:
  52. response = requests.post(url, data=payload, files=files, headers=headers, timeout=30)
  53. resp_json = response.json()
  54. except requests.exceptions.RequestException as e:
  55. raise requests.RequestException(f"上传请求失败: {e}") from e
  56. finally:
  57. files["audio"][1].close()
  58. if response.status_code != 200:
  59. raise ValueError(f"服务端返回错误 (HTTP {response.status_code}): {resp_json.get('message', '未知错误')}")
  60. if not resp_json.get("success", False):
  61. raise ValueError(f"业务失败: {resp_json.get('message', '未知错误')}")
  62. return resp_json
  63. def get_model_info_by_id(self, reference_id: str) -> Dict[str, Any]:
  64. url = f"{self.base_url}/model/{reference_id}"
  65. headers = {
  66. "Authorization": "Bearer 0891f3f93a2640428f9988e267aa57e1",
  67. "Content-Type": "application/json"
  68. }
  69. response = requests.get(url, headers=headers, timeout=(10, 1800))
  70. return response.json()
  71. def is_official_api(self):
  72. return "api.fish.audio" in self.base_url
  73. @staticmethod
  74. def build_common_header() -> Dict[str, Any]:
  75. return {
  76. "Accept": "application/json",
  77. }