From ac16b46aaa6e5ab55196cd726ee7647c7fc57bdb Mon Sep 17 00:00:00 2001 From: Rain-0x01-39 <83620631+Rain-0x01-39@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:46:13 +0800 Subject: [PATCH 1/4] fix(gsvi_tts): Use the correct calling method (#5638) Add some configuration items for GSVI --- astrbot/core/config/default.py | 9 +- .../core/provider/sources/gsvi_tts_source.py | 95 +++++++++++++++---- 2 files changed, 80 insertions(+), 24 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 246914ee0c..87f605e192 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -1623,10 +1623,13 @@ class ChatProviderTemplate(TypedDict): "type": "gsvi_tts_api", "provider": "gpt_sovits_inference", "provider_type": "text_to_speech", - "api_base": "http://127.0.0.1:5000", - "character": "", - "emotion": "default", "enable": False, + "api_base": "http://127.0.0.1:8000", + "version": "v4", + "character": "", + "prompt_text_lang": "中文", + "emotion": "默认", + "text_lang": "中文", "timeout": 20, }, "FishAudio TTS(API)": { diff --git a/astrbot/core/provider/sources/gsvi_tts_source.py b/astrbot/core/provider/sources/gsvi_tts_source.py index 425e801f46..c2d0fa1391 100644 --- a/astrbot/core/provider/sources/gsvi_tts_source.py +++ b/astrbot/core/provider/sources/gsvi_tts_source.py @@ -1,6 +1,5 @@ -import os -import urllib.parse import uuid +from pathlib import Path import aiohttp @@ -10,6 +9,42 @@ from ..provider import TTSProvider from ..register import register_provider_adapter +# 贴个作为参考 +# curl -X 'POST' \ +# 'https://gsv2p.acgnai.top/infer_single' \ +# -H 'accept: application/json' \ +# -H 'Authorization: Bearer 2e3d9b7*************************' \ +# -H 'Content-Type: application/json' \ +# -d '{ +# "version": "v4", +# "model_name": "原神-中文-芙宁娜_ZH", +# "prompt_text_lang": "中文", +# "emotion": "默认", +# "text": "还是面向人工智能编程的", +# "text_lang": "中文", +# "top_k": 10, +# "top_p": 1, +# "temperature": 1, +# "text_split_method": "按标点符号切", +# "batch_size": 1, +# "batch_threshold": 0.75, +# "split_bucket": true, +# "speed_facter": 1, +# "fragment_interval": 0.3, +# "media_type": "wav", +# "parallel_infer": true, +# "repetition_penalty": 1.35, +# "seed": -1, +# "sample_steps": 16, +# "if_sr": false +# }' + +# { +# "msg": "合成成功", +# "audio_url": "https://gsv2p.acgnai.top/outputs/9264dd20655fa31660376801639c9a73.wav" +# } + +# {"msg":"参数错误","audio_url":""} @register_provider_adapter( "gsvi_tts_api", @@ -23,37 +58,55 @@ def __init__( provider_settings: dict, ) -> None: super().__init__(provider_config, provider_settings) - self.api_base = provider_config.get("api_base", "http://127.0.0.1:5000") + self.api_key = provider_config.get( + "api_key" + ) + self.api_base = provider_config.get("api_base", "http://127.0.0.1:8000") self.api_base = self.api_base.removesuffix("/") + self.version = provider_config.get("version", "v4") self.character = provider_config.get("character") - self.emotion = provider_config.get("emotion") + self.prompt_text_lang = provider_config.get("prompt_text_lang", "中文") + self.emotion = provider_config.get("emotion", "默认") + self.text_lang = provider_config.get("text_lang", "中文") async def get_audio(self, text: str) -> str: temp_dir = get_astrbot_temp_path() - path = os.path.join(temp_dir, f"gsvi_tts_{uuid.uuid4()}.wav") - params = {"text": text} - - if self.character: - params["character"] = self.character - if self.emotion: - params["emotion"] = self.emotion - - query_parts = [] - for key, value in params.items(): - encoded_value = urllib.parse.quote(str(value)) - query_parts.append(f"{key}={encoded_value}") + path = Path(temp_dir) / f"gsvi_tts_{uuid.uuid4()}.wav" + url = f"{self.api_base}/infer_single" - url = f"{self.api_base}/tts?{'&'.join(query_parts)}" + data = { + "dl_url": self.api_base, + "version": self.version, + "model_name": self.character, + "prompt_text_lang": self.prompt_text_lang, + "emotion": self.emotion, + "text": text, + "text_lang": self.text_lang, + } async with aiohttp.ClientSession() as session: - async with session.get(url) as response: + async with session.post( + url, json=data, headers={"Authorization": f"Bearer {self.api_key}"} + ) as response: if response.status == 200: - with open(path, "wb") as f: - f.write(await response.read()) + resp_json = await response.json() + msg = resp_json.get("msg") + audio_url = resp_json.get("audio_url") + if not msg or msg != "合成成功": + raise Exception(f"GSVI TTS API 合成失败: {msg}") + async with session.get(audio_url) as audio_response: + if audio_response.status == 200: + with open(path, "wb") as f: + f.write(await audio_response.read()) + else: + error_text = await audio_response.text() + raise Exception( + f"GSVI TTS API 下载音频失败,状态码: {audio_response.status},错误: {error_text}", + ) else: error_text = await response.text() raise Exception( f"GSVI TTS API 请求失败,状态码: {response.status},错误: {error_text}", ) - return path + return str(path) From eb000bf8bea95d3fb74b3c884a92b79d40efd7ee Mon Sep 17 00:00:00 2001 From: Rain-0x01-39 <83620631+Rain-0x01-39@users.noreply.github.com> Date: Sat, 28 Mar 2026 16:59:05 +0800 Subject: [PATCH 2/4] fix(gsvi_tts): add default value for api_key in provider configuration --- astrbot/core/config/default.py | 1 + astrbot/core/provider/sources/gsvi_tts_source.py | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 87f605e192..49def4a577 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -1624,6 +1624,7 @@ class ChatProviderTemplate(TypedDict): "provider": "gpt_sovits_inference", "provider_type": "text_to_speech", "enable": False, + "api_key": "", "api_base": "http://127.0.0.1:8000", "version": "v4", "character": "", diff --git a/astrbot/core/provider/sources/gsvi_tts_source.py b/astrbot/core/provider/sources/gsvi_tts_source.py index c2d0fa1391..1896eced26 100644 --- a/astrbot/core/provider/sources/gsvi_tts_source.py +++ b/astrbot/core/provider/sources/gsvi_tts_source.py @@ -58,9 +58,7 @@ def __init__( provider_settings: dict, ) -> None: super().__init__(provider_config, provider_settings) - self.api_key = provider_config.get( - "api_key" - ) + self.api_key = provider_config.get("api_key", "") self.api_base = provider_config.get("api_base", "http://127.0.0.1:8000") self.api_base = self.api_base.removesuffix("/") self.version = provider_config.get("version", "v4") From d7b0a72497f489ab92d4b77b9662cd1c6fec90f7 Mon Sep 17 00:00:00 2001 From: Rain-0x01-39 <83620631+Rain-0x01-39@users.noreply.github.com> Date: Sat, 28 Mar 2026 17:18:53 +0800 Subject: [PATCH 3/4] fix(gsvi_tts): Adjust wherever the Authorization header is built to only include it when `self.api_key` is truthy Delete some comments --- .../core/provider/sources/gsvi_tts_source.py | 42 +++---------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/astrbot/core/provider/sources/gsvi_tts_source.py b/astrbot/core/provider/sources/gsvi_tts_source.py index 1896eced26..3d759ecd75 100644 --- a/astrbot/core/provider/sources/gsvi_tts_source.py +++ b/astrbot/core/provider/sources/gsvi_tts_source.py @@ -9,42 +9,6 @@ from ..provider import TTSProvider from ..register import register_provider_adapter -# 贴个作为参考 -# curl -X 'POST' \ -# 'https://gsv2p.acgnai.top/infer_single' \ -# -H 'accept: application/json' \ -# -H 'Authorization: Bearer 2e3d9b7*************************' \ -# -H 'Content-Type: application/json' \ -# -d '{ -# "version": "v4", -# "model_name": "原神-中文-芙宁娜_ZH", -# "prompt_text_lang": "中文", -# "emotion": "默认", -# "text": "还是面向人工智能编程的", -# "text_lang": "中文", -# "top_k": 10, -# "top_p": 1, -# "temperature": 1, -# "text_split_method": "按标点符号切", -# "batch_size": 1, -# "batch_threshold": 0.75, -# "split_bucket": true, -# "speed_facter": 1, -# "fragment_interval": 0.3, -# "media_type": "wav", -# "parallel_infer": true, -# "repetition_penalty": 1.35, -# "seed": -1, -# "sample_steps": 16, -# "if_sr": false -# }' - -# { -# "msg": "合成成功", -# "audio_url": "https://gsv2p.acgnai.top/outputs/9264dd20655fa31660376801639c9a73.wav" -# } - -# {"msg":"参数错误","audio_url":""} @register_provider_adapter( "gsvi_tts_api", @@ -72,6 +36,10 @@ async def get_audio(self, text: str) -> str: path = Path(temp_dir) / f"gsvi_tts_{uuid.uuid4()}.wav" url = f"{self.api_base}/infer_single" + headers = {"Content-Type": "application/json"} + if self.api_key: + headers["Authorization"] = f"Bearer {self.api_key}" + data = { "dl_url": self.api_base, "version": self.version, @@ -84,7 +52,7 @@ async def get_audio(self, text: str) -> str: async with aiohttp.ClientSession() as session: async with session.post( - url, json=data, headers={"Authorization": f"Bearer {self.api_key}"} + url, json=data, headers=headers ) as response: if response.status == 200: resp_json = await response.json() From 4387e535986d4a80a2a5fbf70029a540fc7b73cf Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sat, 28 Mar 2026 20:48:16 +0800 Subject: [PATCH 4/4] chore: ruff format --- astrbot/core/provider/sources/gsvi_tts_source.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/astrbot/core/provider/sources/gsvi_tts_source.py b/astrbot/core/provider/sources/gsvi_tts_source.py index 3d759ecd75..55a0975de6 100644 --- a/astrbot/core/provider/sources/gsvi_tts_source.py +++ b/astrbot/core/provider/sources/gsvi_tts_source.py @@ -51,9 +51,7 @@ async def get_audio(self, text: str) -> str: } async with aiohttp.ClientSession() as session: - async with session.post( - url, json=data, headers=headers - ) as response: + async with session.post(url, json=data, headers=headers) as response: if response.status == 200: resp_json = await response.json() msg = resp_json.get("msg")