asr.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import asyncio
  2. import json
  3. import time
  4. import websockets
  5. import aiofiles
  6. from fastapi import APIRouter, UploadFile, WebSocket
  7. from datetime import datetime
  8. from app.config.config import settings
  9. from app.models.ai_meeting import AIMeetingModel, AIMeetingStatus
  10. from app.models.ai_meeting_minutes import AIMeetingMinutesRecordModel, AIMeetingSpeecher
  11. from app.models.asr import AsrRecordModel, AsrRecordStatus
  12. from app.models.common import Callback, Page
  13. from app.services.ai_meeting import AIMeetingService
  14. from app.services.ai_meeting_minutes import AIMeetingMinutesRecordService
  15. from app.services.asr import AsrRecordService
  16. from app.services.hotword import ReplaceHotwordService, ASRHotwordService
  17. from app.utils.common import uuid_generate, text_replace, match_content_keyword
  18. from app.utils.savefile import save_file, StreamFileSaver, rename_to_md5, FileStatus
  19. # from app.utils.audio import convert_to_mp3
  20. from app.utils.oss import upload_file
  21. router = APIRouter()
  22. WS_URL = f"ws://{settings.meeting_file_base_url}/ws/transcribe"
  23. LANG = "zh"
  24. SV = 1 # 1 for enabled, 0 for disabled
  25. SPEAKER_VERIFICATION = 1
  26. @router.websocket("/realtime")
  27. async def realtime(websocket: WebSocket):
  28. await websocket.accept()
  29. times = 0
  30. pause = 0
  31. start = ""
  32. saver: StreamFileSaver = StreamFileSaver()
  33. is_update = False
  34. content_list = []
  35. try:
  36. async with websockets.connect(WS_URL+ f"?lang={LANG}&sv={int(SPEAKER_VERIFICATION)}") as ws:
  37. # 接收前端传输的音频流
  38. data = await websocket.receive_json()
  39. await saver.aset_ext(data["ext"])
  40. meeting_id = data["meeting_id"]
  41. meeting = await AIMeetingService.get_ai_meeting(meeting_id)
  42. if not meeting or meeting.status != AIMeetingStatus.RECRODING:
  43. await websocket.send_json(
  44. {"type": "error", "data": "会议信息异常!"}
  45. )
  46. raise Exception("meeting error")
  47. is_update = True
  48. hotword_dict = {i.origin_word:i.hotword for i in await ReplaceHotwordService.list_replace_hotwords(Page(page_size=1000))}
  49. hotwords = " ".join([i.hotword for i in
  50. await ASRHotwordService.list_asr_hotwords(Page(page_size=1000))])
  51. await ws.send(hotwords) # 发送文件到实时ASR服务
  52. async def receive():
  53. nonlocal pause
  54. async with saver:
  55. while True:
  56. audio_data = await websocket.receive_bytes()
  57. await ws.send(audio_data) # 发送文件到实时ASR服务
  58. saver.audio_data_list.append(audio_data)
  59. try:
  60. message = await asyncio.wait_for(websocket.receive_json(), timeout=0.1)
  61. if message.get("action") == "pause":
  62. pause_time = int(time.time())
  63. while True:
  64. # 接收前端传输的音频流
  65. tmp_data = await websocket.receive_json()
  66. if tmp_data.get("action") == "start":
  67. pause += int(time.time()) - pause_time
  68. break
  69. except Exception:
  70. ...
  71. async def send():
  72. start_time = int(time.time())
  73. nonlocal times
  74. nonlocal start
  75. async for message in ws:
  76. try:
  77. res_json = json.loads(message)
  78. if res_json.get('code') == 0:
  79. recv_data = res_json.get('data', {})
  80. if recv_data:
  81. speaker = recv_data.get("speaker")
  82. text = recv_data.get("text")
  83. if text:
  84. text = await text_replace(text, hotword_dict)
  85. print(f"user:{speaker}---{text}")
  86. start = datetime.now().strftime('%Y/%m/%d %H:%M:%S')
  87. times = int(time.time()) - start_time - pause
  88. send_json = {"type": "stream", "text": text, "times": times, "start": start, "speaker":speaker}
  89. await websocket.send_json(
  90. send_json
  91. )
  92. # await websocket.send_json(
  93. # {"type": "close", "data": ""}
  94. # )
  95. content_list.append(send_json)
  96. await AIMeetingService.update_ai_meeting_record(
  97. AIMeetingModel(id=meeting_id, content=send_json)
  98. )
  99. else:
  100. print("Error:", res_json.get('code'), res_json.get('message'))
  101. except json.JSONDecodeError as e:
  102. print("Failed to parse response data:", e)
  103. # 接收实时ASR的结果
  104. await asyncio.gather(receive(), send())
  105. # except WebSocketDisconnect:
  106. # print("---------------------关闭连接11111-------------------------")
  107. except Exception as e:
  108. print("------------------------------------------------")
  109. finally:
  110. print("---------------------关闭连接:finally-------------------------")
  111. try:
  112. await ws.close()
  113. except:
  114. ...
  115. try:
  116. await websocket.close()
  117. except:
  118. ...
  119. if is_update:
  120. print("----------------更新状态------------------------")
  121. object_name = f"{await uuid_generate()}{saver.ext}"
  122. if saver.status != FileStatus.CLOSED:
  123. await saver.__aexit__(None, None, None)
  124. duration = await saver.get_duration(saver.get_file_path())
  125. if duration:
  126. file_url = await upload_file(object_name, saver.get_file_path())
  127. minutes_record = await AIMeetingMinutesRecordService.generate_meeting_summary(AIMeetingMinutesRecordModel(view_content=content_list))
  128. print(minutes_record)
  129. await AIMeetingMinutesRecordService.create_ai_meeting_minutes_record(AIMeetingMinutesRecordModel(meeting_id=meeting_id, content=minutes_record.get("summary", "生成会议记录失败!"), speecher=AIMeetingSpeecher.AI))
  130. await AIMeetingService.update_ai_meeting(
  131. AIMeetingModel(id=meeting_id, file_url=file_url, duration=duration, description= await match_content_keyword(minutes_record.get("summary", "")),
  132. status=AIMeetingStatus.FINISHED)
  133. )
  134. else:
  135. await AIMeetingService.update_ai_meeting(
  136. AIMeetingModel(id=meeting_id, file_url="", duration=duration,
  137. status=AIMeetingStatus.FAILED)
  138. )
  139. # await saver.remove_file(saver.get_file_path())
  140. @router.post("/callback")
  141. async def callback(call_back: Callback):
  142. task_id = call_back.task_id
  143. result = call_back.result
  144. # print(result)
  145. hotword_dict = {i.origin_word: i.hotword for i in
  146. await ReplaceHotwordService.list_replace_hotwords(Page(page_size=1000))}
  147. if task_id and isinstance(result, list):
  148. view_list = [
  149. {"type": "stream", "text": await text_replace(i["text"], hotword_dict), "times": i["start"] // 1000 if isinstance(i["start"], int) else 0,
  150. "start": "", "speaker": i["speaker"]} for i in result
  151. ]
  152. print(view_list)
  153. asr_record = await AsrRecordService.update_asr_record(AsrRecordModel(
  154. task_id=task_id,
  155. status=AsrRecordStatus.FINISHED
  156. ))
  157. minutes_record = await AIMeetingMinutesRecordService.generate_meeting_summary(
  158. AIMeetingMinutesRecordModel(view_content=view_list))
  159. print(minutes_record)
  160. # if minutes_record:
  161. await AIMeetingMinutesRecordService.create_ai_meeting_minutes_record(
  162. AIMeetingMinutesRecordModel(meeting_id=asr_record.meeting_id, content=minutes_record.get("summary", "生成会议记录失败!"),
  163. speecher=AIMeetingSpeecher.AI))
  164. await AIMeetingService.update_ai_meeting(
  165. AIMeetingModel(id=asr_record.meeting_id, view_content=json.dumps(view_list), status=AIMeetingStatus.FINISHED,
  166. description=await match_content_keyword(minutes_record.get("summary", ""))))
  167. print(f"Received callback for task {task_id}: {result}")
  168. return {"status": "success"}