修改设备端对接
This commit is contained in:
36
talkingq-url/handlers/audio_file_handler.py
Normal file
36
talkingq-url/handlers/audio_file_handler.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import os
|
||||
import uuid
|
||||
from config import settings
|
||||
from utils.logger import session_logger
|
||||
from utils.audio_denoiser import reduce_background_noise
|
||||
|
||||
|
||||
async def save_audio_file(audio_data: bytes, device_id: str) -> str:
|
||||
"""
|
||||
保存音频数据到 assets/audio 目录
|
||||
|
||||
Args:
|
||||
audio_data: 音频二进制数据
|
||||
device_id: 设备ID
|
||||
|
||||
Returns:
|
||||
音频文件的相对路径
|
||||
"""
|
||||
try:
|
||||
audio_dir = os.path.join(settings.assets_dir, "audio")
|
||||
os.makedirs(audio_dir, exist_ok=True)
|
||||
|
||||
filename = f"{device_id}_{uuid.uuid4().hex[:8]}.mp3"
|
||||
filepath = os.path.join(audio_dir, filename)
|
||||
|
||||
with open(filepath, 'wb') as f:
|
||||
f.write(audio_data)
|
||||
|
||||
# relative_path = f"assets/audio/{filename}"
|
||||
session_logger.info(device_id, "audio", f"音频文件已保存: {filepath}")
|
||||
|
||||
# reduce_background_noise(filepath, relative_path,noise_path='assets/audio/noise_sample.wav',normalize_volume=True)
|
||||
return filepath
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "audio", f"保存音频文件时出错: {e}", exc_info=True)
|
||||
raise
|
||||
@@ -2,6 +2,7 @@ from fastapi import WebSocket, WebSocketDisconnect
|
||||
from utils.logger import session_logger
|
||||
from services.connection_manager import connection_manager
|
||||
from services.task_manager import task_manager
|
||||
from services.offline_audio_cache import offline_audio_cache
|
||||
from handlers.websocket_auth_handler import authenticate_websocket
|
||||
from handlers.websocket_message_handler import handle_websocket_messages
|
||||
from handlers.session_cleanup_handler import cleanup_device_sessions
|
||||
@@ -29,6 +30,20 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||
await websocket.send_text('{"status": "error", "message": "Not authenticated"}')
|
||||
return
|
||||
|
||||
# 检查设备是否有离线音频URL需要发送
|
||||
has_pending = await offline_audio_cache.has_pending_audio(device_id)
|
||||
if has_pending:
|
||||
audio_urls = await offline_audio_cache.get_audio_urls(device_id)
|
||||
for audio_url in audio_urls:
|
||||
try:
|
||||
await websocket.send_text(f"SOUND_URL:{audio_url}")
|
||||
session_logger.info(device_id, "offline", f"发送离线音频URL: {audio_url}")
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "offline", f"发送离线音频URL失败: {e}")
|
||||
# 清空已发送的离线音频URL
|
||||
await offline_audio_cache.clear_audio_urls(device_id)
|
||||
session_logger.info(device_id, "offline", f"已清空设备的离线音频URL缓存,共 {len(audio_urls)} 个")
|
||||
|
||||
await handle_websocket_messages(websocket, device_id)
|
||||
|
||||
except WebSocketDisconnect:
|
||||
@@ -43,7 +58,7 @@ async def websocket_endpoint(websocket: WebSocket):
|
||||
)
|
||||
finally:
|
||||
if device_id:
|
||||
await connection_manager.remove_connection(device_id)
|
||||
# await connection_manager.remove_connection(device_id)
|
||||
await cleanup_device_sessions(device_id)
|
||||
# 清理设备相关的所有异步任务
|
||||
await task_manager.cancel_device_tasks(device_id)
|
||||
|
||||
@@ -4,12 +4,19 @@ import asyncio
|
||||
from fastapi import WebSocket
|
||||
from handlers.audio_packet_parser import parse_packet
|
||||
from handlers.audio_session_handler import handle_websocket_data
|
||||
from handlers.audio_file_handler import save_audio_file
|
||||
from services.audio_session import audio_session_manager
|
||||
from services.interrupt_handler import interrupt_handler
|
||||
from services.task_manager import task_manager
|
||||
from services.connection_manager import connection_manager
|
||||
from services.device_target_cache import device_target_cache
|
||||
from services.offline_audio_cache import offline_audio_cache
|
||||
from services.target_audio_cache import target_audio_cache
|
||||
from services.card_service import card_service
|
||||
from utils.logger import session_logger
|
||||
from handlers.prompt_sound_handler import handle_prompt_sound_request
|
||||
from handlers.session_cleanup_handler import handle_old_session_cleanup
|
||||
from config import settings
|
||||
|
||||
async def handle_websocket_messages(websocket: WebSocket, device_id: str):
|
||||
"""
|
||||
@@ -52,7 +59,36 @@ async def handle_websocket_messages(websocket: WebSocket, device_id: str):
|
||||
|
||||
async def handle_text_message(websocket: WebSocket, device_id: str, text_data: str):
|
||||
"""处理文本消息"""
|
||||
if text_data.startswith("REQUEST_PROMPT_SOUND:"):
|
||||
if text_data.startswith("REGISTER_TARGET_DEVICE:"):
|
||||
card_uuid = text_data.split(":", 1)[1].strip()
|
||||
|
||||
# 检查卡片是否存在
|
||||
existing_card = await card_service.get_card_by_uuid(card_uuid)
|
||||
|
||||
if existing_card:
|
||||
# 卡片已存在,使用卡片绑定的设备ID作为目标设备ID
|
||||
target_device_id = existing_card.device_id
|
||||
session_logger.info(device_id, "card", f"卡片已存在,绑定的设备ID: {target_device_id}")
|
||||
else:
|
||||
# 卡片不存在,创建新卡片并绑定到当前设备
|
||||
new_card = await card_service.activate_card(card_uuid, device_id)
|
||||
session_logger.info(device_id, "card", f"新卡片{card_uuid}已创建并激活,绑定到设备: {device_id}")
|
||||
return
|
||||
|
||||
# 设置目标设备
|
||||
await device_target_cache.set_target(device_id, target_device_id)
|
||||
|
||||
# 检查目标设备是否在线
|
||||
target_websocket = await connection_manager.get_connection(target_device_id)
|
||||
if target_websocket and target_websocket.client_state.name == "CONNECTED":
|
||||
sound_url = f"http://{settings.server_host}:{settings.server_port}/assets/audio/welcome.mp3"
|
||||
await websocket.send_text(f"TARGET_DEVICE_REGISTERED_URL:{sound_url}")
|
||||
session_logger.info(device_id, "target", f"成功注册目标设备: {target_device_id}")
|
||||
else:
|
||||
sound_url = f"http://{settings.server_host}:{settings.server_port}/assets/audio/offline.mp3"
|
||||
await websocket.send_text(f"TARGET_DEVICE_REGISTERED_URL:{sound_url}")
|
||||
session_logger.warning(device_id, "target", f"目标设备 {target_device_id} 不在线")
|
||||
elif text_data.startswith("REQUEST_PROMPT_SOUND:"):
|
||||
prompt_type = text_data.split(":", 1)[1]
|
||||
if device_id:
|
||||
await handle_prompt_sound_request(device_id, prompt_type)
|
||||
@@ -107,16 +143,36 @@ async def handle_binary_message(websocket: WebSocket, device_id: str, binary_dat
|
||||
session_id = session_key[1]
|
||||
|
||||
if packet_type == 1: # 开始包
|
||||
target_device_id = await device_target_cache.get_target(device_id)
|
||||
if target_device_id:
|
||||
# 清除音频缓存
|
||||
await target_audio_cache.clear_audio_data(target_device_id)
|
||||
return None, None
|
||||
current_active_session = await handle_start_packet(
|
||||
device_id, session_id, session_key, session, current_active_session
|
||||
)
|
||||
elif packet_type == 3: # 中断包
|
||||
target_device_id = await device_target_cache.get_target(device_id)
|
||||
if target_device_id:
|
||||
return None, None
|
||||
current_active_session = await handle_interrupt_packet(
|
||||
device_id, session_id, session_key, session, current_active_session
|
||||
)
|
||||
elif packet_type == 2: # 结束包
|
||||
target_device_id = await device_target_cache.get_target(device_id)
|
||||
if target_device_id:
|
||||
# 处理缓存的音频数据
|
||||
session_logger.info(device_id, session_id, f"收到结束包,开始处理缓存音频数据")
|
||||
await process_cached_audio(device_id, target_device_id)
|
||||
|
||||
# 移除目标设备关联
|
||||
await device_target_cache.remove_target(device_id)
|
||||
return None, None
|
||||
if current_active_session == session_key:
|
||||
current_active_session = None
|
||||
elif packet_type == 4: # 发送给目标设备的音频包
|
||||
await handle_target_audio_packet(device_id, audio_data)
|
||||
return None, None
|
||||
elif packet_type == 0 and first_audio_received_time is None:
|
||||
first_audio_received_time = time.perf_counter()
|
||||
session.start_time = first_audio_received_time
|
||||
@@ -178,3 +234,53 @@ async def handle_interrupt_packet(device_id, session_id, session_key, session, c
|
||||
task_type="interrupt"
|
||||
)
|
||||
return current_active_session
|
||||
|
||||
|
||||
async def handle_target_audio_packet(device_id: str, audio_data: bytes):
|
||||
"""处理发送给目标设备的音频包"""
|
||||
try:
|
||||
target_device_id = await device_target_cache.get_target(device_id)
|
||||
|
||||
if not target_device_id:
|
||||
session_logger.warning(device_id, "target", "未设置目标设备,无法发送音频")
|
||||
return
|
||||
|
||||
# 缓存音频数据
|
||||
await target_audio_cache.add_audio_data(target_device_id, audio_data)
|
||||
session_logger.info(device_id, "target", f"已缓存音频数据到目标设备 {target_device_id}")
|
||||
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "target", f"处理目标音频包时出错: {e}", exc_info=True)
|
||||
|
||||
|
||||
async def process_cached_audio(device_id: str, target_device_id: str):
|
||||
"""处理缓存的音频数据并发送音频URL"""
|
||||
try:
|
||||
# 获取缓存的音频数据
|
||||
cached_audio = await target_audio_cache.get_audio_data(target_device_id)
|
||||
if not cached_audio:
|
||||
session_logger.info(device_id, "target", f"目标设备 {target_device_id} 没有缓存的音频数据")
|
||||
return
|
||||
|
||||
# 保存音频文件
|
||||
audio_path = await save_audio_file(cached_audio, device_id)
|
||||
audio_url = f"http://{settings.server_host}:{settings.server_port}/{audio_path}"
|
||||
|
||||
# 发送URL给目标设备
|
||||
target_websocket = await connection_manager.get_connection(target_device_id)
|
||||
if target_websocket and target_websocket.client_state.name == "CONNECTED":
|
||||
await target_websocket.send_text("TTS_START")
|
||||
session_logger.info(device_id, "target", "已发送 TTS_START 给客户端")
|
||||
await target_websocket.send_text(f"NFC_SOUND_URL:{audio_url}")
|
||||
session_logger.info(device_id, "target", f"已发送音频URL给目标设备 {target_device_id}: {audio_url}")
|
||||
await target_websocket.send_text("TTS_END")
|
||||
session_logger.info(device_id, "target", "已发送 TTS_END 给客户端")
|
||||
else:
|
||||
# 目标设备不在线,保存到离线缓存
|
||||
await offline_audio_cache.add_audio_url(target_device_id, audio_url)
|
||||
session_logger.warning(device_id, "target", f"目标设备 {target_device_id} 不在线,保存音频URL到离线缓存")
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "target", f"处理缓存音频时出错: {e}", exc_info=True)
|
||||
finally:
|
||||
# 清除缓存
|
||||
await target_audio_cache.clear_audio_data(target_device_id)
|
||||
|
||||
Reference in New Issue
Block a user