Files
banban/talkingq-url/services/audio_sender.py
2026-03-24 15:04:36 +08:00

166 lines
6.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
)