audio_parser.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from core.router import Parser
  2. from models.result import ParseResult
  3. from utils.logger import log
  4. from utils.ffmpeg_wrapper import FFmpegWrapper
  5. import os
  6. import tempfile
  7. import requests
  8. class AudioParser(Parser):
  9. """音频文件解析器"""
  10. def __init__(self):
  11. self.ffmpeg = FFmpegWrapper()
  12. # Qwen3-ASR模型配置 - 使用专门的音频转录端点
  13. self.qwen_asr_api_url = "http://10.192.72.13:7283/v1/audio/transcriptions"
  14. log.info("音频解析器初始化完成,使用本地部署的Qwen3-ASR模型")
  15. async def parse(self, file_path: str) -> ParseResult:
  16. """
  17. 解析音频文件
  18. Args:
  19. file_path: 文件路径
  20. Returns:
  21. ParseResult: 解析结果
  22. """
  23. log.info(f"开始解析音频文件: {file_path}")
  24. temp_wav_path = None
  25. try:
  26. # 1. 预处理:转换为16k/16bit/mono wav
  27. with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as temp_file:
  28. temp_wav_path = temp_file.name
  29. log.info(f"创建临时文件: {temp_wav_path}")
  30. self.ffmpeg.convert_audio(file_path, temp_wav_path)
  31. log.info(f"音频转换完成: {temp_wav_path}")
  32. # 2. 使用Qwen3-ASR进行语音识别
  33. log.info("开始使用Qwen3-ASR进行语音识别...")
  34. log.info(f"使用的API地址: {self.qwen_asr_api_url}")
  35. content = []
  36. result = None
  37. try:
  38. # 检查文件大小
  39. file_size = os.path.getsize(temp_wav_path)
  40. log.info(f"音频文件大小: {file_size / (1024 * 1024):.2f} MB")
  41. # 使用专门的音频转录端点,支持文件上传
  42. log.info("准备使用文件上传方式进行语音识别...")
  43. # 构建请求数据
  44. session = requests.Session()
  45. session.trust_env = False # 禁用环境变量中的代理设置
  46. # 使用multipart/form-data上传文件
  47. files = {
  48. 'file': ('audio.wav', open(temp_wav_path, 'rb'), 'audio/wav')
  49. }
  50. data = {
  51. 'model': '/data/shared/Qwen3-ASR/qwen/Qwen3-ASR-1.7B',
  52. 'language': 'zh',
  53. 'response_format': 'json'
  54. }
  55. # 发送请求
  56. log.info("开始发送请求...")
  57. # 增加超时时间,音频处理可能需要更长时间
  58. response = session.post(self.qwen_asr_api_url, files=files, data=data, timeout=600)
  59. log.info(f"请求完成,状态码: {response.status_code}")
  60. # 打印响应内容以进行调试
  61. if response.status_code != 200:
  62. log.warning(f"响应内容: {response.text}")
  63. response.raise_for_status()
  64. result = response.json()
  65. log.info("Qwen3-ASR语音识别完成")
  66. log.info(f"识别结果: {result}")
  67. # 3. 构建解析结果
  68. if result and 'text' in result:
  69. full_text = result['text']
  70. # 清理识别结果中的标记
  71. clean_text = full_text.replace('language Chinese<asr_text>', '').strip()
  72. content.append(f"完整文本: {clean_text}")
  73. else:
  74. log.warning("解析失败:未获取到有效结果")
  75. content.append("解析失败:未获取到有效结果")
  76. log.info(f"构建完成,内容长度: {len(content)}")
  77. except requests.exceptions.Timeout as e:
  78. log.error(f"Qwen3-ASR语音识别超时: {str(e)}")
  79. content.append("语音识别超时,请检查服务是否正常运行")
  80. except requests.exceptions.ConnectionError as e:
  81. log.error(f"Qwen3-ASR语音识别连接错误: {str(e)}")
  82. content.append("语音识别连接错误,请检查服务地址是否正确")
  83. except Exception as e:
  84. log.error(f"Qwen3-ASR语音识别失败: {str(e)}")
  85. import traceback
  86. log.error(f"异常堆栈: {traceback.format_exc()}")
  87. # 即使失败,也尝试返回一个基本结果
  88. content.append("语音识别失败,但文件已成功处理")
  89. # 清理临时文件
  90. if temp_wav_path and os.path.exists(temp_wav_path):
  91. os.remove(temp_wav_path)
  92. log.info(f"临时文件已清理: {temp_wav_path}")
  93. return ParseResult(
  94. content="\n".join(content),
  95. metadata={
  96. "parser": "Qwen3-ASR",
  97. "file_size": os.path.getsize(file_path),
  98. "api_url": self.qwen_asr_api_url
  99. },
  100. file_type="audio"
  101. )
  102. except Exception as e:
  103. log.error(f"音频文件解析失败: {str(e)}")
  104. import traceback
  105. log.error(f"异常堆栈: {traceback.format_exc()}")
  106. # 清理临时文件
  107. if temp_wav_path and os.path.exists(temp_wav_path):
  108. try:
  109. os.remove(temp_wav_path)
  110. log.info(f"临时文件已清理: {temp_wav_path}")
  111. except:
  112. pass
  113. return ParseResult(
  114. content="",
  115. metadata={"error": str(e)},
  116. file_type="audio"
  117. )