Сервис для хранения файлов аудио, индексации файлов, записи и выдачи результатов распознавания
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

264 lines
8.8 KiB

from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks, status
from sqlalchemy.orm import Session
from typing import Dict, Any
import uuid
import os
import asyncio
from apiApp.database import get_db, Audio
from apiApp.schemas import (
RecognitionStartResponse,
RecognitionStatus,
ErrorResponse
)
from apiApp.services import AudioCRUD, AiConclusionCRUD
from apiApp.config import UPLOAD_FOLDER
router = APIRouter()
# Глобальное хранилище статусов задач (в продакшене лучше использовать Redis)
recognition_tasks: Dict[str, Dict[str, Any]] = {}
async def process_recognition(audio_id: uuid.UUID, file_path: str, task_id: str):
"""
Фоновая задача для распознавания аудио
"""
try:
# Обновляем статус на processing
recognition_tasks[task_id] = {
'audio_id': audio_id,
'status': 'processing',
'result': None,
'error': None
}
# Проверяем существование файла
if not os.path.exists(file_path):
recognition_tasks[task_id]['status'] = 'error'
recognition_tasks[task_id]['error'] = 'File not found on disk'
return
# Здесь должна быть реальная логика распознавания
# Например, вызов внешнего API или локальной модели
# result = await your_recognize_function(file_path)
# Симуляция обработки (в реальном коде убрать)
await asyncio.sleep(2)
# Пример результата (заменить на реальный)
result = {
'text': 'Распознанный текст из аудио',
'confidence': 0.95,
'duration': 120.5,
'segments': [
{
'start': 0.0,
'end': 5.2,
'text': 'Привет, чем могу помочь?',
'speaker': 'SPEAKER_00'
},
{
'start': 5.5,
'end': 10.8,
'text': 'Мне нужна информация о услугах',
'speaker': 'SPEAKER_01'
}
]
}
# Обновляем статус на completed
recognition_tasks[task_id]['status'] = 'completed'
recognition_tasks[task_id]['result'] = result
# Получаем сессию БД (для фоновой задачи)
from apiApp.database import SessionLocal
db = SessionLocal()
try:
# Создаем или обновляем AI Conclusion
conclusion = AiConclusionCRUD.get_by_audio_id(db, audio_id)
if conclusion:
AiConclusionCRUD.update(
db=db,
conclusion_id=conclusion.id,
conclusion_data={
"transcription": result.get('segments', []),
"ai_transcription": [result.get('text', '')],
"conclusion": {
"confidence": result.get('confidence', 0.0),
"duration": result.get('duration', 0.0)
}
},
end_date=True
)
else:
AiConclusionCRUD.create(
db=db,
audio_id=audio_id,
conclusion={
"transcription": result.get('segments', []),
"ai_transcription": [result.get('text', '')],
"conclusion": {
"confidence": result.get('confidence', 0.0),
"duration": result.get('duration', 0.0)
}
}
)
# Обновляем запись аудио
AudioCRUD.update_recognition_result(db, audio_id, result)
finally:
db.close()
except Exception as e:
recognition_tasks[task_id]['status'] = 'error'
recognition_tasks[task_id]['error'] = str(e)
@router.post("/recognize/{audio_id}", response_model=RecognitionStartResponse, status_code=status.HTTP_202_ACCEPTED)
async def start_recognition(
audio_id: uuid.UUID,
background_tasks: BackgroundTasks,
db: Session = Depends(get_db)
):
"""
Запуск распознавания аудиофайла
"""
# Проверяем существование аудио
audio = AudioCRUD.get_by_id(db, audio_id)
if not audio:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Audio not found in database"
)
# Проверяем, нет ли уже активной задачи
for task_id, task in recognition_tasks.items():
if task['audio_id'] == audio_id and task['status'] == 'processing':
return RecognitionStartResponse(
status="info",
message="Recognition already in progress",
task_id=task_id,
audio_id=audio_id
)
# Проверяем наличие файла
if not audio.file_path or not os.path.exists(audio.file_path):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Audio file not found on disk"
)
# Создаем task_id
task_id = str(uuid.uuid4())
# Добавляем фоновую задачу
background_tasks.add_task(process_recognition, audio_id, audio.file_path, task_id)
return RecognitionStartResponse(
status="success",
message="Recognition started",
task_id=task_id,
audio_id=audio_id
)
@router.get("/recognize/{audio_id}", response_model=RecognitionStatus)
async def get_recognition_status(
audio_id: uuid.UUID,
db: Session = Depends(get_db)
):
"""
Получение статуса распознавания по audio_id
"""
# Проверяем существование аудио
audio = AudioCRUD.get_by_id(db, audio_id)
if not audio:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Audio not found"
)
# Ищем задачу для данного audio_id
task_info = None
for task_id, task in recognition_tasks.items():
if task['audio_id'] == audio_id:
task_info = {
'task_id': task_id,
**task
}
break
if not task_info:
# Проверяем, есть ли сохраненный результат
conclusion = AiConclusionCRUD.get_by_audio_id(db, audio_id)
if conclusion and conclusion.end_date:
return RecognitionStatus(
task_id="",
audio_id=audio_id,
status="completed",
result=conclusion.conclusion
)
return RecognitionStatus(
task_id="",
audio_id=audio_id,
status="not_started"
)
return RecognitionStatus(**task_info)
@router.get("/recognize/task/{task_id}", response_model=RecognitionStatus)
async def get_recognition_task(task_id: str):
"""
Получение статуса задачи по task_id
"""
if task_id not in recognition_tasks:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Task not found"
)
task = recognition_tasks[task_id]
return RecognitionStatus(
task_id=task_id,
audio_id=task['audio_id'],
status=task['status'],
result=task.get('result'),
error=task.get('error')
)
@router.get("/recognize/{audio_id}/result")
async def get_recognition_result(
audio_id: uuid.UUID,
db: Session = Depends(get_db)
):
"""
Получение результата распознавания из базы данных
"""
audio = AudioCRUD.get_by_id(db, audio_id)
if not audio:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Audio not found"
)
conclusion = AiConclusionCRUD.get_by_audio_id(db, audio_id)
if not conclusion or not conclusion.end_date:
return {
"status": "not_available",
"message": "Recognition result not available yet",
"audio_id": str(audio_id)
}
return {
"status": "success",
"audio_id": str(audio_id),
"result": conclusion.conclusion,
"index_date": conclusion.index_date,
"end_date": conclusion.end_date
}