修复设备给家长留言音频云存储链路
This commit is contained in:
@@ -140,10 +140,10 @@ class DeviceVoiceArchiveService(DatabaseServiceBase):
|
||||
),
|
||||
)
|
||||
stored_audio = await message_audio_storage_service.upload_audio(
|
||||
sender_device_id=sender_device_id,
|
||||
receiver_device_id=receiver_device_id,
|
||||
device_id=sender_device_id,
|
||||
content=archive_audio_data,
|
||||
content_type=prepared_audio.mime_type,
|
||||
extension=prepared_audio.archive_format,
|
||||
)
|
||||
session_logger.info(
|
||||
sender_device_id,
|
||||
|
||||
@@ -142,3 +142,6 @@ class MessageAudioStorageService:
|
||||
ContentType=content_type,
|
||||
EnableMD5=False,
|
||||
)
|
||||
|
||||
|
||||
message_audio_storage_service = MessageAudioStorageService()
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
from banban.service.message_audio_storage import MessageAudioStorageService
|
||||
from banban.service.message_audio_storage import StoredMessageAudio, message_audio_storage_service
|
||||
from utils.logger import session_logger
|
||||
|
||||
|
||||
message_audio_storage_service = MessageAudioStorageService()
|
||||
|
||||
|
||||
# async def save_audio_file(audio_data: bytes, device_id: str) -> str:
|
||||
# """Upload device audio to COS and return its object key."""
|
||||
# try:
|
||||
# stored = await message_audio_storage_service.upload_audio(
|
||||
# device_id=device_id,
|
||||
# content=audio_data,
|
||||
# )
|
||||
# session_logger.info(device_id, "audio", f"audio uploaded to COS: {stored.file_key}")
|
||||
# return stored.file_key
|
||||
# except Exception as e:
|
||||
# session_logger.error(device_id, "audio", f"failed to store audio: {e}", exc_info=True)
|
||||
# raise
|
||||
async def upload_message_audio(
|
||||
audio_data: bytes,
|
||||
device_id: str,
|
||||
*,
|
||||
content_type: str = "audio/mpeg",
|
||||
extension: str = "mp3",
|
||||
) -> StoredMessageAudio:
|
||||
"""Upload message audio to COS and return its storage metadata."""
|
||||
try:
|
||||
stored = await message_audio_storage_service.upload_audio(
|
||||
device_id=device_id,
|
||||
content=audio_data,
|
||||
content_type=content_type,
|
||||
extension=extension,
|
||||
)
|
||||
session_logger.info(device_id, "audio", f"audio uploaded to COS: {stored.file_key}")
|
||||
return stored
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "audio", f"failed to store audio: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
|
||||
import os
|
||||
@@ -54,4 +59,4 @@ async def save_audio_file(audio_data: bytes, device_id: str) -> str:
|
||||
return filepath
|
||||
except Exception as e:
|
||||
session_logger.error(device_id, "audio", f"保存音频文件时出错: {e}", exc_info=True)
|
||||
raise
|
||||
raise
|
||||
|
||||
@@ -4,7 +4,7 @@ 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 message_audio_storage_service, save_audio_file
|
||||
from handlers.audio_file_handler import message_audio_storage_service, save_audio_file, upload_message_audio
|
||||
from services.audio_session import audio_session_manager
|
||||
from services.interrupt_handler import interrupt_handler
|
||||
from services.task_manager import task_manager
|
||||
@@ -19,8 +19,19 @@ from handlers.prompt_sound_handler import handle_prompt_sound_request
|
||||
from handlers.session_cleanup_handler import handle_old_session_cleanup
|
||||
from config import settings
|
||||
from banban.service.im import im_service as im_conversation_service
|
||||
from utils.audio_format import detect_audio_format, wrap_pcm_as_wav
|
||||
from fastapi import HTTPException
|
||||
|
||||
|
||||
def prepare_message_audio(audio_data: bytes) -> tuple[bytes, str, str, str]:
|
||||
source_format = detect_audio_format(audio_data)
|
||||
if source_format == "wav":
|
||||
return audio_data, "audio/wav", "wav", source_format
|
||||
if source_format == "mp3":
|
||||
return audio_data, "audio/mpeg", "mp3", source_format
|
||||
return wrap_pcm_as_wav(audio_data), "audio/wav", "wav", source_format
|
||||
|
||||
|
||||
async def handle_websocket_messages(websocket: WebSocket, device_id: str, serial_number: str):
|
||||
"""
|
||||
处理WebSocket连接中的所有消息
|
||||
@@ -266,16 +277,30 @@ async def process_parent_leave_message(device_id: str, audio_cache_key: str):
|
||||
session_logger.info(device_id, "parent", "发给家长的留言没有缓存音频数据")
|
||||
return
|
||||
|
||||
audio_file_key = await save_audio_file(cached_audio, device_id)
|
||||
audio_url = f"http://{settings.server_host}:{settings.server_port}/{audio_file_key}"
|
||||
archive_audio, media_mime_type, extension, source_format = prepare_message_audio(cached_audio)
|
||||
stored_audio = await upload_message_audio(
|
||||
archive_audio,
|
||||
device_id,
|
||||
content_type=media_mime_type,
|
||||
extension=extension,
|
||||
)
|
||||
await im_conversation_service.create_device_parent_leave_message(
|
||||
device_id=device_id,
|
||||
media_file_key=audio_url,
|
||||
media_mime_type="audio/mpeg",
|
||||
media_size_bytes=len(cached_audio),
|
||||
ext_json={"source": "device_ws_parent_leave_message"},
|
||||
media_file_key=stored_audio.file_key,
|
||||
media_mime_type=media_mime_type,
|
||||
media_size_bytes=len(archive_audio),
|
||||
ext_json={
|
||||
"source": "device_ws_parent_leave_message",
|
||||
"storage": "cos",
|
||||
"source_format": source_format,
|
||||
"archive_format": extension,
|
||||
},
|
||||
)
|
||||
session_logger.info(
|
||||
device_id,
|
||||
"parent",
|
||||
f"发给家长的留言已上传COS并写入家长会话: {stored_audio.file_key}",
|
||||
)
|
||||
session_logger.info(device_id, "parent", "发给家长的留言已写入家长会话")
|
||||
|
||||
websocket = await connection_manager.get_connection(device_id)
|
||||
if websocket and websocket.client_state.name == "CONNECTED":
|
||||
|
||||
33
talkingq-url/utils/audio_format.py
Normal file
33
talkingq-url/utils/audio_format.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import io
|
||||
import wave
|
||||
|
||||
|
||||
DEFAULT_SAMPLE_RATE = 16000
|
||||
DEFAULT_CHANNELS = 1
|
||||
DEFAULT_SAMPLE_WIDTH = 2
|
||||
|
||||
|
||||
def detect_audio_format(audio_data: bytes) -> str:
|
||||
if len(audio_data) >= 12 and audio_data[:4] == b"RIFF" and audio_data[8:12] == b"WAVE":
|
||||
return "wav"
|
||||
if audio_data.startswith(b"ID3"):
|
||||
return "mp3"
|
||||
if len(audio_data) >= 2 and audio_data[0] == 0xFF and (audio_data[1] & 0xE0) == 0xE0:
|
||||
return "mp3"
|
||||
return "pcm_s16le_16k_mono"
|
||||
|
||||
|
||||
def wrap_pcm_as_wav(
|
||||
audio_data: bytes,
|
||||
*,
|
||||
sample_rate: int = DEFAULT_SAMPLE_RATE,
|
||||
channels: int = DEFAULT_CHANNELS,
|
||||
sample_width: int = DEFAULT_SAMPLE_WIDTH,
|
||||
) -> bytes:
|
||||
wav_buffer = io.BytesIO()
|
||||
with wave.open(wav_buffer, "wb") as wav_file:
|
||||
wav_file.setnchannels(channels)
|
||||
wav_file.setsampwidth(sample_width)
|
||||
wav_file.setframerate(sample_rate)
|
||||
wav_file.writeframes(audio_data)
|
||||
return wav_buffer.getvalue()
|
||||
Reference in New Issue
Block a user