265 lines
8.8 KiB
Python
265 lines
8.8 KiB
Python
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
|
|
}
|