173 lines
6.5 KiB
Python
173 lines
6.5 KiB
Python
"""
|
||
音频处理服务
|
||
|
||
处理已保存的完整音频文件:数据库更新、转录、自动总结
|
||
"""
|
||
from fastapi import BackgroundTasks
|
||
from app.core.database import get_db_connection
|
||
from app.core.response import create_api_response
|
||
from app.core.config import BASE_DIR
|
||
from app.services.async_transcription_service import AsyncTranscriptionService
|
||
from app.services.async_meeting_service import async_meeting_service
|
||
from pathlib import Path
|
||
import os
|
||
|
||
|
||
transcription_service = AsyncTranscriptionService()
|
||
|
||
|
||
def handle_audio_upload(
|
||
file_path: str,
|
||
file_name: str,
|
||
file_size: int,
|
||
meeting_id: int,
|
||
current_user: dict,
|
||
auto_summarize: bool = True,
|
||
background_tasks: BackgroundTasks = None,
|
||
prompt_id: int = None
|
||
) -> dict:
|
||
"""
|
||
处理已保存的完整音频文件
|
||
|
||
职责:
|
||
1. 权限检查
|
||
2. 检查已有文件和转录记录
|
||
3. 更新数据库(audio_files 表)
|
||
4. 启动转录任务
|
||
5. 可选启动自动总结监控
|
||
|
||
Args:
|
||
file_path: 已保存的文件路径(相对于 BASE_DIR 的路径,如 /uploads/audio/123/xxx.webm)
|
||
file_name: 原始文件名
|
||
file_size: 文件大小(字节)
|
||
meeting_id: 会议ID
|
||
current_user: 当前用户信息
|
||
auto_summarize: 是否自动生成总结(默认True)
|
||
background_tasks: FastAPI 后台任务对象
|
||
prompt_id: 提示词模版ID(可选,如果不指定则使用默认模版)
|
||
|
||
Returns:
|
||
dict: {
|
||
"success": bool, # 是否成功
|
||
"response": dict, # 如果需要返回,这里是响应数据
|
||
"file_info": dict, # 文件信息 (成功时)
|
||
"transcription_task_id": str, # 转录任务ID (成功时)
|
||
"replaced_existing": bool, # 是否替换了现有文件 (成功时)
|
||
"has_transcription": bool # 原来是否有转录记录 (成功时)
|
||
}
|
||
"""
|
||
print(f"[Audio Service] handle_audio_upload called - Meeting ID: {meeting_id}, Auto-summarize: {auto_summarize}, Received prompt_id: {prompt_id}, Type: {type(prompt_id)}")
|
||
|
||
# 1. 权限和已有文件检查
|
||
try:
|
||
with get_db_connection() as connection:
|
||
cursor = connection.cursor(dictionary=True)
|
||
|
||
# 检查会议是否存在及权限
|
||
cursor.execute("SELECT user_id FROM meetings WHERE meeting_id = %s", (meeting_id,))
|
||
meeting = cursor.fetchone()
|
||
if not meeting:
|
||
return {
|
||
"success": False,
|
||
"response": create_api_response(code="404", message="会议不存在")
|
||
}
|
||
if meeting['user_id'] != current_user['user_id']:
|
||
return {
|
||
"success": False,
|
||
"response": create_api_response(code="403", message="无权限操作此会议")
|
||
}
|
||
|
||
# 检查已有音频文件
|
||
cursor.execute(
|
||
"SELECT file_name, file_path, upload_time FROM audio_files WHERE meeting_id = %s",
|
||
(meeting_id,)
|
||
)
|
||
existing_info = cursor.fetchone()
|
||
|
||
# 检查是否有转录记录
|
||
has_transcription = False
|
||
if existing_info:
|
||
cursor.execute(
|
||
"SELECT COUNT(*) as segment_count FROM transcript_segments WHERE meeting_id = %s",
|
||
(meeting_id,)
|
||
)
|
||
has_transcription = cursor.fetchone()['segment_count'] > 0
|
||
|
||
cursor.close()
|
||
except Exception as e:
|
||
return {
|
||
"success": False,
|
||
"response": create_api_response(code="500", message=f"检查已有文件失败: {str(e)}")
|
||
}
|
||
|
||
# 2. 删除旧的音频文件(如果存在)
|
||
replaced_existing = existing_info is not None
|
||
if replaced_existing and existing_info['file_path']:
|
||
old_file_path = BASE_DIR / existing_info['file_path'].lstrip('/')
|
||
if old_file_path.exists():
|
||
try:
|
||
os.remove(old_file_path)
|
||
print(f"Deleted old audio file: {old_file_path}")
|
||
except Exception as e:
|
||
print(f"Warning: Failed to delete old file {old_file_path}: {e}")
|
||
|
||
transcription_task_id = None
|
||
|
||
try:
|
||
# 3. 更新数据库记录
|
||
with get_db_connection() as connection:
|
||
cursor = connection.cursor(dictionary=True)
|
||
|
||
if replaced_existing:
|
||
cursor.execute(
|
||
'UPDATE audio_files SET file_name = %s, file_path = %s, file_size = %s, upload_time = NOW(), task_id = NULL WHERE meeting_id = %s',
|
||
(file_name, file_path, file_size, meeting_id)
|
||
)
|
||
else:
|
||
cursor.execute(
|
||
'INSERT INTO audio_files (meeting_id, file_name, file_path, file_size, upload_time) VALUES (%s, %s, %s, %s, NOW())',
|
||
(meeting_id, file_name, file_path, file_size)
|
||
)
|
||
|
||
connection.commit()
|
||
cursor.close()
|
||
|
||
# 4. 启动转录任务
|
||
try:
|
||
transcription_task_id = transcription_service.start_transcription(meeting_id, file_path)
|
||
print(f"Transcription task {transcription_task_id} started for meeting {meeting_id}")
|
||
|
||
# 5. 如果启用自动总结且提供了 background_tasks,添加监控任务
|
||
if auto_summarize and transcription_task_id and background_tasks:
|
||
background_tasks.add_task(
|
||
async_meeting_service.monitor_and_auto_summarize,
|
||
meeting_id,
|
||
transcription_task_id,
|
||
prompt_id # 传递 prompt_id 给自动总结监控任务
|
||
)
|
||
print(f"[audio_service] Auto-summarize enabled, monitor task added for meeting {meeting_id}, prompt_id: {prompt_id}")
|
||
|
||
except Exception as e:
|
||
print(f"Failed to start transcription: {e}")
|
||
raise
|
||
|
||
except Exception as e:
|
||
# 出错时的处理(文件已保存,不删除)
|
||
return {
|
||
"success": False,
|
||
"response": create_api_response(code="500", message=f"处理失败: {str(e)}")
|
||
}
|
||
|
||
# 6. 返回成功结果
|
||
return {
|
||
"success": True,
|
||
"file_info": {
|
||
"file_name": file_name,
|
||
"file_path": file_path,
|
||
"file_size": file_size
|
||
},
|
||
"transcription_task_id": transcription_task_id,
|
||
"replaced_existing": replaced_existing,
|
||
"has_transcription": has_transcription
|
||
}
|