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