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
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 |
|
}
|
|
|