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