diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 70122a282..528e8f899 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -971,6 +971,7 @@ CONFIG_METADATA_2 = { "api_key": "", "api_base": "https://api.fish.audio/v1", "fishaudio-tts-character": "可莉", + "fishaudio-tts-reference-id": "", "timeout": "20", }, "阿里云百炼 TTS(API)": { @@ -1564,6 +1565,11 @@ CONFIG_METADATA_2 = { "type": "string", "hint": "fishaudio TTS 的角色。默认为可莉。更多角色请访问:https://fish.audio/zh-CN/discovery", }, + "fishaudio-tts-reference-id": { + "description": "reference_id", + "type": "string", + "hint": "fishaudio TTS 的参考模型ID(可选)。如果填入此字段,将直接使用模型ID而不通过角色名称查询。例如:626bb6d3f3364c9cbc3aa6a67300a664。更多模型请访问:https://fish.audio/zh-CN/discovery,进入模型详情界面后可复制模型ID", + }, "whisper_hint": { "description": "本地部署 Whisper 模型须知", "type": "string", diff --git a/astrbot/core/provider/sources/fishaudio_tts_api_source.py b/astrbot/core/provider/sources/fishaudio_tts_api_source.py index c0cf044b8..893f27463 100644 --- a/astrbot/core/provider/sources/fishaudio_tts_api_source.py +++ b/astrbot/core/provider/sources/fishaudio_tts_api_source.py @@ -1,5 +1,6 @@ import os import uuid +import re import ormsgpack from pydantic import BaseModel, conint from httpx import AsyncClient @@ -24,8 +25,8 @@ class ServeTTSRequest(BaseModel): # 参考音频 references: list[ServeReferenceAudio] = [] # 参考模型 ID - # 例如 https://fish.audio/m/7f92f8afb8ec43bf81429cc1c9199cb1/ - # 其中reference_id为 7f92f8afb8ec43bf81429cc1c9199cb1 + # 例如 https://fish.audio/m/626bb6d3f3364c9cbc3aa6a67300a664/ + # 其中reference_id为 626bb6d3f3364c9cbc3aa6a67300a664 reference_id: str | None = None # 对中英文文本进行标准化,这可以提高数字的稳定性 normalize: bool = True @@ -44,6 +45,7 @@ class ProviderFishAudioTTSAPI(TTSProvider): ) -> None: super().__init__(provider_config, provider_settings) self.chosen_api_key: str = provider_config.get("api_key", "") + self.reference_id: str = provider_config.get("fishaudio-tts-reference-id", "") self.character: str = provider_config.get("fishaudio-tts-character", "可莉") self.api_base: str = provider_config.get( "api_base", "https://api.fish-audio.cn/v1" @@ -81,11 +83,43 @@ class ProviderFishAudioTTSAPI(TTSProvider): return item["_id"] return None + def _validate_reference_id(self, reference_id: str) -> bool: + """ + 验证reference_id格式是否有效 + + Args: + reference_id: 参考模型ID + + Returns: + bool: ID是否有效 + """ + if not reference_id or not reference_id.strip(): + return False + + # FishAudio的reference_id通常是32位十六进制字符串 + # 例如: 626bb6d3f3364c9cbc3aa6a67300a664 + pattern = r'^[a-fA-F0-9]{32}$' + return bool(re.match(pattern, reference_id.strip())) + async def _generate_request(self, text: str) -> dict: + # 向前兼容逻辑:优先使用reference_id,如果没有则使用角色名称查询 + if self.reference_id and self.reference_id.strip(): + # 验证reference_id格式 + if not self._validate_reference_id(self.reference_id): + raise ValueError( + f"无效的FishAudio参考模型ID: '{self.reference_id}'. " + f"请确保ID是32位十六进制字符串(例如: 626bb6d3f3364c9cbc3aa6a67300a664)。" + f"您可以从 https://fish.audio/zh-CN/discovery 获取有效的模型ID。" + ) + reference_id = self.reference_id.strip() + else: + # 回退到原来的角色名称查询逻辑 + reference_id = await self._get_reference_id_by_character(self.character) + return ServeTTSRequest( text=text, format="wav", - reference_id=await self._get_reference_id_by_character(self.character), + reference_id=reference_id, ) async def get_audio(self, text: str) -> str: