From 59ef51ea20e85e5a33a39e6de3e623d8745d52c6 Mon Sep 17 00:00:00 2001 From: XL Date: Tue, 6 May 2025 14:19:05 +0800 Subject: [PATCH 1/5] =?UTF-8?q?tts=20=E5=A4=B1=E8=B4=A5=E9=87=8D=E8=AF=95b?= =?UTF-8?q?ug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/tts/cozecn.py | 16 ++++++++----- .../core/providers/tts/custom.py | 4 +++- .../xiaozhi-server/core/providers/tts/edge.py | 24 +++++++++++-------- .../core/providers/tts/fishspeech.py | 4 +++- .../core/providers/tts/gpt_sovits_v2.py | 6 ++--- .../core/providers/tts/gpt_sovits_v3.py | 7 +++--- .../core/providers/tts/siliconflow.py | 15 +++++++----- .../core/providers/tts/ttson.py | 22 +++++++++-------- 8 files changed, 58 insertions(+), 40 deletions(-) diff --git a/main/xiaozhi-server/core/providers/tts/cozecn.py b/main/xiaozhi-server/core/providers/tts/cozecn.py index 1f9244f6..56314f4f 100644 --- a/main/xiaozhi-server/core/providers/tts/cozecn.py +++ b/main/xiaozhi-server/core/providers/tts/cozecn.py @@ -38,9 +38,13 @@ class TTSProvider(TTSProviderBase): "Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json", } - response = requests.request( - "POST", self.api_url, json=request_json, headers=headers - ) - data = response.content - file_to_save = open(output_file, "wb") - file_to_save.write(data) + + try: + response = requests.request( + "POST", self.api_url, json=request_json, headers=headers + ) + data = response.content + file_to_save = open(output_file, "wb") + file_to_save.write(data) + except Exception as e: + raise Exception(f"{__name__} error: {e}") \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/custom.py b/main/xiaozhi-server/core/providers/tts/custom.py index 3417790f..5b8669da 100644 --- a/main/xiaozhi-server/core/providers/tts/custom.py +++ b/main/xiaozhi-server/core/providers/tts/custom.py @@ -32,4 +32,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error(f"Custom TTS请求失败: {resp.status_code} - {resp.text}") + error_msg = f"Custom TTS请求失败: {resp.status_code} - {resp.text}" + logger.bind(tag=TAG).error(error_msg) + raise Exception(error_msg) # 抛出异常,让调用方捕获 diff --git a/main/xiaozhi-server/core/providers/tts/edge.py b/main/xiaozhi-server/core/providers/tts/edge.py index b86e4087..3d9b2547 100644 --- a/main/xiaozhi-server/core/providers/tts/edge.py +++ b/main/xiaozhi-server/core/providers/tts/edge.py @@ -20,14 +20,18 @@ class TTSProvider(TTSProviderBase): ) async def text_to_speak(self, text, output_file): - communicate = edge_tts.Communicate(text, voice=self.voice) - # 确保目录存在并创建空文件 - os.makedirs(os.path.dirname(output_file), exist_ok=True) - with open(output_file, "wb") as f: - pass + try: + communicate = edge_tts.Communicate(text, voice=self.voice) + # 确保目录存在并创建空文件 + os.makedirs(os.path.dirname(output_file), exist_ok=True) + with open(output_file, "wb") as f: + pass - # 流式写入音频数据 - with open(output_file, "ab") as f: # 改为追加模式避免覆盖 - async for chunk in communicate.stream(): - if chunk["type"] == "audio": # 只处理音频数据块 - f.write(chunk["data"]) + # 流式写入音频数据 + with open(output_file, "ab") as f: # 改为追加模式避免覆盖 + async for chunk in communicate.stream(): + if chunk["type"] == "audio": # 只处理音频数据块 + f.write(chunk["data"]) + except Exception as e: + error_msg = f"Edge TTS请求失败: {e}" + raise Exception(error_msg) # 抛出异常,让调用方捕获 \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/fishspeech.py b/main/xiaozhi-server/core/providers/tts/fishspeech.py index 316dbed7..627c6e99 100644 --- a/main/xiaozhi-server/core/providers/tts/fishspeech.py +++ b/main/xiaozhi-server/core/providers/tts/fishspeech.py @@ -177,5 +177,7 @@ class TTSProvider(TTSProviderBase): audio_file.write(audio_content) else: - print(f"Request failed with status code {response.status_code}") + error_msg = f"Request failed with status code {response.status_code}" + print(error_msg) print(response.json()) + raise Exception(error_msg) diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py index b2aad88a..2d58679d 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v2.py @@ -105,6 +105,6 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error( - f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}" - ) + error_msg = f"GPT_SoVITS_V2 TTS请求失败: {resp.status_code} - {resp.text}" + logger.bind(tag=TAG).error(error_msg) + raise Exception(error_msg) diff --git a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py index 488280f1..d4da23ed 100644 --- a/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py +++ b/main/xiaozhi-server/core/providers/tts/gpt_sovits_v3.py @@ -64,6 +64,7 @@ class TTSProvider(TTSProviderBase): with open(output_file, "wb") as file: file.write(resp.content) else: - logger.bind(tag=TAG).error( - f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}" - ) + error_msg = f"GPT_SoVITS_V3 TTS请求失败: {resp.status_code} - {resp.text}" + logger.bind(tag=TAG).error(error_msg) + raise Exception(error_msg) + diff --git a/main/xiaozhi-server/core/providers/tts/siliconflow.py b/main/xiaozhi-server/core/providers/tts/siliconflow.py index b2f564cc..9e30f721 100644 --- a/main/xiaozhi-server/core/providers/tts/siliconflow.py +++ b/main/xiaozhi-server/core/providers/tts/siliconflow.py @@ -39,9 +39,12 @@ class TTSProvider(TTSProviderBase): "Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json", } - response = requests.request( - "POST", self.api_url, json=request_json, headers=headers - ) - data = response.content - file_to_save = open(output_file, "wb") - file_to_save.write(data) + try: + response = requests.request( + "POST", self.api_url, json=request_json, headers=headers + ) + data = response.content + file_to_save = open(output_file, "wb") + file_to_save.write(data) + except Exception as e: + raise Exception(f"{__name__} error: {e}") \ No newline at end of file diff --git a/main/xiaozhi-server/core/providers/tts/ttson.py b/main/xiaozhi-server/core/providers/tts/ttson.py index 047d7a67..f3a0fbdc 100644 --- a/main/xiaozhi-server/core/providers/tts/ttson.py +++ b/main/xiaozhi-server/core/providers/tts/ttson.py @@ -58,8 +58,8 @@ class TTSProvider(TTSProviderBase): resp = requests.request("POST", url, data=payload) if resp.status_code != 200: - logger.bind(tag=TAG).error(f"TTS请求失败: {resp.text}") - return None + logger.bind(tag=TAG).error(f"TTSON 请求失败: {resp.text}") + raise Exception(f"{__name__}: TTS请求失败") resp_json = resp.json() try: result = ( @@ -71,13 +71,15 @@ class TTSProvider(TTSProviderBase): + "&voice_audio_path=" + resp_json["voice_path"] ) + + audio_content = requests.get(result) + with open(output_file, "wb") as f: + f.write(audio_content.content) + return True + voice_path = resp_json.get("voice_path") + des_path = output_file + shutil.move(voice_path, des_path) + except Exception as e: print("error:", e) - - audio_content = requests.get(result) - with open(output_file, "wb") as f: - f.write(audio_content.content) - return True - voice_path = resp_json.get("voice_path") - des_path = output_file - shutil.move(voice_path, des_path) + raise Exception(f"{__name__}: TTS请求失败") \ No newline at end of file From e7054ea13fbb198ce8d6fa27f9f3b947759c07c9 Mon Sep 17 00:00:00 2001 From: CGD <3030332422@qq.com> Date: Wed, 7 May 2025 14:36:08 +0800 Subject: [PATCH 2/5] =?UTF-8?q?update:=E9=87=8D=E5=90=AF=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E5=8A=9F=E8=83=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/connection.py | 33 +++++++++++++++++++ main/xiaozhi-server/core/handle/textHandle.py | 3 ++ 2 files changed, 36 insertions(+) diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 060de4c9..8a3fea26 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -1,6 +1,7 @@ import os import copy import json +import sys import uuid import time import queue @@ -233,6 +234,38 @@ class ConnectionHandler: elif isinstance(message, bytes): await handleAudioMessage(self, message) + async def handle_restart(self, message): + """处理服务器重启请求""" + try: + + self.logger.bind(tag=TAG).info("收到服务器重启指令,准备执行...") + + # 发送确认响应 + await self.websocket.send(json.dumps({ + "type": "server_response", + "status": "success", + "message": "服务器重启中..." + })) + + # 异步执行重启操作 + def restart_server(): + """实际执行重启的方法""" + time.sleep(1) + self.logger.bind(tag=TAG).info("执行服务器重启...") + python = sys.executable + os.execl(python, python, *sys.argv) + + # 使用线程执行重启避免阻塞事件循环 + threading.Thread(target=restart_server, daemon=True).start() + + except Exception as e: + self.logger.bind(tag=TAG).error(f"重启失败: {str(e)}") + await self.websocket.send(json.dumps({ + "type": "server_response", + "status": "error", + "message": f"Restart failed: {str(e)}" + })) + def _initialize_components(self, private_config): """初始化组件""" if private_config is not None: diff --git a/main/xiaozhi-server/core/handle/textHandle.py b/main/xiaozhi-server/core/handle/textHandle.py index 0bded15b..61ef92c3 100644 --- a/main/xiaozhi-server/core/handle/textHandle.py +++ b/main/xiaozhi-server/core/handle/textHandle.py @@ -136,5 +136,8 @@ async def handleTextMessage(conn, message): } ) ) + # 重启服务器 + elif msg_json["action"] == "restart": + await conn.handle_restart(msg_json) except json.JSONDecodeError: await conn.websocket.send(message) From fa56d06e0c79d7fe6a309b63a46f094057bc373c Mon Sep 17 00:00:00 2001 From: CGD <3030332422@qq.com> Date: Wed, 7 May 2025 16:17:50 +0800 Subject: [PATCH 3/5] =?UTF-8?q?update=EF=BC=9A=E4=BC=98=E5=8C=96=E9=87=8D?= =?UTF-8?q?=E5=90=AF=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/connection.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 8a3fea26..603ed7ae 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -1,6 +1,7 @@ import os import copy import json +import subprocess import sys import uuid import time @@ -252,8 +253,14 @@ class ConnectionHandler: """实际执行重启的方法""" time.sleep(1) self.logger.bind(tag=TAG).info("执行服务器重启...") - python = sys.executable - os.execl(python, python, *sys.argv) + subprocess.Popen( + [sys.executable, "app.py"], + stdin=sys.stdin, + stdout=sys.stdout, + stderr=sys.stderr, + start_new_session=True + ) + os._exit(0) # 使用线程执行重启避免阻塞事件循环 threading.Thread(target=restart_server, daemon=True).start() From ee18fbebae163695ee8764a38e661c36a4ba17b5 Mon Sep 17 00:00:00 2001 From: CGD <3030332422@qq.com> Date: Wed, 7 May 2025 16:20:43 +0800 Subject: [PATCH 4/5] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E5=A4=8D=E2=80=9C?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E8=BF=90=E8=A1=8C=E6=97=B6=E6=9C=AA?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=A0=87=E5=87=86=E8=BE=93=E5=85=A5=EF=BC=88?= =?UTF-8?q?stdin=EF=BC=89=EF=BC=8C=E5=AF=BC=E8=87=B4=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E8=A2=AB=E7=BC=93=E5=86=B2=EF=BC=8C=E7=9B=B4=E5=88=B0=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E7=BB=88=E6=AD=A2=E5=90=8E=E6=89=8D=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E2=80=9D=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/app.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/main/xiaozhi-server/app.py b/main/xiaozhi-server/app.py index 4ad57855..619fb5c7 100644 --- a/main/xiaozhi-server/app.py +++ b/main/xiaozhi-server/app.py @@ -7,6 +7,7 @@ from core.ota_server import SimpleOtaServer from core.utils.util import check_ffmpeg_installed from config.logger import setup_logging from core.utils.util import get_local_ip +from aioconsole import ainput TAG = __name__ logger = setup_logging() @@ -34,10 +35,19 @@ async def wait_for_exit() -> None: pass +async def monitor_stdin(): + """监控标准输入,消费回车键""" + while True: + await ainput() # 异步等待输入,消费回车 + + async def main(): check_ffmpeg_installed() config = load_config() + # 添加 stdin 监控任务 + stdin_task = asyncio.create_task(monitor_stdin()) + # 启动 WebSocket 服务器 ws_server = WebSocketServer(config) ws_task = asyncio.create_task(ws_server.start()) @@ -78,19 +88,22 @@ async def main(): ) try: - await wait_for_exit() # 监听退出信号 + await wait_for_exit() # 阻塞直到收到退出信号 except asyncio.CancelledError: print("任务被取消,清理资源中...") finally: + # 取消所有任务(关键修复点) + stdin_task.cancel() ws_task.cancel() if ota_task: ota_task.cancel() - try: - await ws_task - if ota_task: - await ota_task - except asyncio.CancelledError: - pass + + # 等待任务终止(必须加超时) + await asyncio.wait( + [stdin_task, ws_task, ota_task] if ota_task else [stdin_task, ws_task], + timeout=3.0, + return_when=asyncio.ALL_COMPLETED + ) print("服务器已关闭,程序退出。") From 7635c68ccd2f16e5e3d39f068d6e43530638c292 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Fri, 9 May 2025 18:01:36 +0800 Subject: [PATCH 5/5] =?UTF-8?q?update:=E5=A2=9E=E5=8A=A0aioconsole?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/requirements.txt b/main/xiaozhi-server/requirements.txt index 02fbfcdb..05c51e8e 100755 --- a/main/xiaozhi-server/requirements.txt +++ b/main/xiaozhi-server/requirements.txt @@ -27,4 +27,5 @@ cnlunar==0.2.0 PySocks==1.7.1 dashscope==1.23.1 baidu-aip==4.16.13 -chardet==5.2.0 \ No newline at end of file +chardet==5.2.0 +aioconsole==0.8.1 \ No newline at end of file