Сервис для хранения файлов аудио, индексации файлов, записи и выдачи результатов распознавания
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.
 
 
 

125 lines
5.3 KiB

from fastapi import APIRouter, Depends, HTTPException, UploadFile, File as FastAPIFile, Form, status
from apiApp.database import get_db
from fastapi.responses import FileResponse
from sqlalchemy.orm import Session
import os, uuid
import logging
from pathlib import Path
import requests
from apiApp.config import ALLOWED_AUDIO_EXTENSIONS, MAX_UPLOAD_SIZE, AUDIOFILES_PATH, GIGAAM_API_URL
import aiofiles
from apiApp.schemas import AudioCreate
from apiApp.services import AudioCRUD
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/external_audio",
tags=["Внешние аудиофайлы"]
)
@router.post("/upload")
async def upload_external_audio(
file: UploadFile = FastAPIFile(...),
callback_url: str = Form(..., description="URL для отправки результата распознавания (FileAudioAPI вызовет GigaAM и затем отправит результат на этот URL)"),
db: Session = Depends(get_db)
):
"""
Загрузка внешнего аудиофайла. Файл сохраняется в общей папке, после чего
автоматически отправляется на распознавание в GigaAM. Результат придёт на callback_url.
"""
# Проверка расширения файла
file_ext = os.path.splitext(file.filename)[1].lower()
if file_ext not in ALLOWED_AUDIO_EXTENSIONS:
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail=f"File extension not allowed. Allowed: {', '.join(ALLOWED_AUDIO_EXTENSIONS)}"
)
content = await file.read()
# Проверка размера файла
if len(content) > MAX_UPLOAD_SIZE:
raise HTTPException(
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
detail=f"File too large. Maximum size: {MAX_UPLOAD_SIZE / (1024*1024)}MB"
)
# Сохранение в общей папке под именем uuid+ext; это же имя передаём в GigaAM
safe_name = f"{uuid.uuid4()}{file_ext}"
upload_dir = Path(AUDIOFILES_PATH) if isinstance(AUDIOFILES_PATH, str) else AUDIOFILES_PATH
upload_dir.mkdir(parents=True, exist_ok=True)
file_path = upload_dir / safe_name
try:
async with aiofiles.open(file_path, 'wb') as f:
await f.write(content)
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error saving file: {str(e)}"
)
# Создание записи в БД (filename = имя файла на диске, его же передаём в GigaAM)
try:
audio_data = AudioCreate(filename=safe_name)
audio = AudioCRUD.create(
db=db,
audio_data=audio_data,
file_path=str(file_path),
file_size=len(content),
sourse="external"
)
except Exception as e:
if os.path.exists(file_path):
os.remove(file_path)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error creating database record: {str(e)}"
)
# Запрос на распознавание в GigaAM (файл уже в общей папке по имени safe_name)
task_id, recognition_error = send_to_recognition(filename=safe_name, callback_url=callback_url.strip())
# Ответ: запись аудио + данные по постановке в очередь распознавания
result = {
"id": str(audio.id),
"filename": audio.filename,
"index_date": audio.index_date.isoformat() if audio.index_date else None,
"file_path": audio.file_path,
"file_size": audio.file_size,
"sourse": audio.sourse,
}
if task_id:
result["recognition_task_id"] = task_id
result["recognition_status_url"] = f"{GIGAAM_API_URL.rstrip('/')}/api/call/task/{task_id}"
if recognition_error:
result["recognition_error"] = recognition_error
return result
def send_to_recognition(filename: str, callback_url: str) -> tuple:
"""
Отправка аудиофайла на распознавание в GigaAM.
Файл должен уже лежать в общей папке под именем filename.
Returns:
(task_id или None, recognition_error или None)
"""
try:
gigaam_url = f"{GIGAAM_API_URL.rstrip('/')}/api/call/external/process"
resp = requests.post(
gigaam_url,
json={"filename": filename, "callback_url": callback_url},
timeout=15,
)
if resp.status_code == 202:
data = resp.json()
task_id = data.get("task_id")
logger.info(f"✅ Внешний файл {filename} поставлен в очередь GigaAM, task_id={task_id}")
return (task_id, None)
recognition_error = resp.text or f"HTTP {resp.status_code}"
logger.warning(f" GigaAM не принял задачу {filename}: {recognition_error}")
return (None, recognition_error)
except requests.exceptions.RequestException as e:
logger.warning(f" Ошибка вызова GigaAM для {filename}: {e}")
return (None, str(e))