166 lines
6.7 KiB
Python
166 lines
6.7 KiB
Python
import asyncio
|
||
from typing import Optional
|
||
from fastapi import WebSocket
|
||
from utils.logger import session_logger
|
||
from services.interrupt_handler import interrupt_handler
|
||
from services.tts_error_manager import tts_error_manager
|
||
from config import settings
|
||
|
||
|
||
class AudioSender:
|
||
|
||
|
||
def __init__(
|
||
self, device_id: str, session_id: str, websocket: Optional[WebSocket] = None
|
||
):
|
||
self.device_id = device_id
|
||
self.session_id = session_id
|
||
self.session_key = (device_id, session_id)
|
||
self.websocket = websocket
|
||
|
||
def check_interruption(self):
|
||
|
||
return interrupt_handler.is_interrupted(self.session_key)
|
||
|
||
def is_websocket_connected(self):
|
||
|
||
return (
|
||
self.websocket is not None
|
||
and self.websocket.client_state.name == "CONNECTED"
|
||
and not getattr(self.websocket, "_closed", False)
|
||
)
|
||
|
||
async def send_start_notification(self):
|
||
|
||
if not self.is_websocket_connected():
|
||
session_logger.warning(
|
||
self.device_id,
|
||
self.session_id,
|
||
"Session websocket 不存在或会话已中断,无法发送 TTS_START",
|
||
)
|
||
await interrupt_handler.set_interrupt_state(self.session_key, True)
|
||
return False
|
||
try:
|
||
await self.websocket.send_text("TTS_START")
|
||
session_logger.info(
|
||
self.device_id, self.session_id, "已发送 TTS_START 给客户端"
|
||
)
|
||
await tts_error_manager.start_tts_session(self.device_id, self.session_id)
|
||
return True
|
||
except Exception as e:
|
||
session_logger.error(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"发送 TTS_START 时发生异常:{e}",
|
||
exc_info=True,
|
||
)
|
||
await interrupt_handler.set_interrupt_state(self.session_key, True)
|
||
return False
|
||
|
||
async def send_audio_urls(self, url_queue: asyncio.Queue):
|
||
|
||
try:
|
||
if not await self.send_start_notification():
|
||
return
|
||
|
||
first_url_sent = False
|
||
first_tts_submit_time = getattr(url_queue, "first_tts_submit_time", None)
|
||
|
||
while True:
|
||
if self.check_interruption():
|
||
session_logger.info(
|
||
self.device_id,
|
||
self.session_id,
|
||
"检测到中断标志,立即停止音频发送任务",
|
||
)
|
||
break
|
||
try:
|
||
audio_url = await asyncio.wait_for(url_queue.get(), timeout=0.1)
|
||
except asyncio.TimeoutError:
|
||
if self.check_interruption():
|
||
break
|
||
continue
|
||
except Exception as e:
|
||
session_logger.error(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"获取音频URL队列数据时出错: {str(e)}",
|
||
)
|
||
break
|
||
if audio_url is None:
|
||
break
|
||
if self.is_websocket_connected() and not self.check_interruption():
|
||
try:
|
||
full_url = f"http://{settings.server_host}:{settings.server_port}/{audio_url}" if not audio_url.startswith('http') else audio_url
|
||
await self.websocket.send_text(f"TTS_URL:{full_url}")
|
||
|
||
if not first_url_sent and first_tts_submit_time is not None:
|
||
first_url_sent = True
|
||
|
||
session_logger.info(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"已发送音频URL给客户端: {full_url}",
|
||
)
|
||
except Exception as e:
|
||
session_logger.error(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"发送音频URL时出错: {str(e)}",
|
||
)
|
||
await interrupt_handler.set_interrupt_state(
|
||
self.session_key, True
|
||
)
|
||
break
|
||
else:
|
||
session_logger.warning(
|
||
self.device_id,
|
||
self.session_id,
|
||
"WebSocket连接已关闭或会话已中断,无法发送音频URL",
|
||
)
|
||
break
|
||
if self.check_interruption():
|
||
break
|
||
url_queue.task_done()
|
||
if not self.check_interruption():
|
||
try:
|
||
if self.is_websocket_connected():
|
||
try:
|
||
await self.websocket.send_text("TTS_END")
|
||
session_logger.info(
|
||
self.device_id,
|
||
self.session_id,
|
||
"已发送 TTS_END 给客户端",
|
||
)
|
||
await tts_error_manager.end_tts_session(self.device_id, self.session_id)
|
||
except RuntimeError as e:
|
||
if "Cannot call" in str(e) and "close message" in str(e):
|
||
session_logger.warning(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"WebSocket已关闭,无法发送TTS_END: {e}",
|
||
)
|
||
else:
|
||
session_logger.error(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"发送TTSEND时发生运行时错误: {e}",
|
||
)
|
||
else:
|
||
session_logger.warning(
|
||
self.device_id,
|
||
self.session_id,
|
||
"WebSocket连接已关闭或会话已中断,跳过发送 TTS_END",
|
||
)
|
||
except Exception as e:
|
||
session_logger.error(
|
||
self.device_id, self.session_id, f"检查WebSocket状态时出错: {e}"
|
||
)
|
||
except Exception as e:
|
||
session_logger.error(
|
||
self.device_id,
|
||
self.session_id,
|
||
f"发送音频URL时出错: {e}",
|
||
exc_info=True,
|
||
)
|