загрузка и распознвание внешний файлов
This commit is contained in:
@@ -14,6 +14,7 @@ class Audio(Base):
|
||||
file_path = Column(Text)
|
||||
duration = Column(Float)
|
||||
file_size = Column(Integer)
|
||||
sourse = Column(Text, default="internal")
|
||||
|
||||
ai_conclusion = relationship("AiConclusion", back_populates="audio", cascade="all, delete-orphan")
|
||||
|
||||
@@ -24,5 +25,6 @@ class Audio(Base):
|
||||
"index_date": self.index_date.isoformat() if self.index_date else None,
|
||||
"file_path": self.file_path,
|
||||
"duration": self.duration,
|
||||
"file_size": self.file_size
|
||||
"file_size": self.file_size,
|
||||
"sourse": self.sourse
|
||||
}
|
||||
@@ -16,6 +16,31 @@ from apiApp.config import WEBHOOK_ENDPOINT, WEBHOOK_API_KEY
|
||||
logger = logging.getLogger(__name__)
|
||||
ai_conclusion_router = APIRouter()
|
||||
|
||||
def _send_callback(callback_url: str, audio: Audio, conclusion_data: Dict[str, Any]) -> None:
|
||||
"""Отправляем результат клиенту по callback_url (не храним в БД)."""
|
||||
try:
|
||||
callback_url = (callback_url or "").strip()
|
||||
if not callback_url:
|
||||
return
|
||||
if not callback_url.startswith(("http://", "https://")):
|
||||
logger.warning(f"⚠️ Некорректный callback_url для {audio.filename}: {callback_url}")
|
||||
return
|
||||
|
||||
payload = {
|
||||
"audio_id": str(audio.id),
|
||||
"filename": audio.filename,
|
||||
"result": conclusion_data
|
||||
}
|
||||
|
||||
resp = requests.post(callback_url, json=payload, timeout=30)
|
||||
if 200 <= resp.status_code < 300:
|
||||
logger.info(f"✅ Callback успешно отправлен для {audio.filename}")
|
||||
else:
|
||||
logger.warning(f"⚠️ Callback вернул статус {resp.status_code} для {audio.filename}")
|
||||
logger.warning(f"Response: {resp.text}")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Ошибка при отправке callback для {audio.filename}: {e}")
|
||||
|
||||
|
||||
class AiConclusionRequest(BaseModel):
|
||||
"""Модель запроса для сохранения AI заключения"""
|
||||
@@ -25,6 +50,7 @@ class AiConclusionRequest(BaseModel):
|
||||
analysis: Dict[str, Any]
|
||||
segments: Optional[List[Dict[str, Any]]] = []
|
||||
processing_time_seconds: Optional[float] = 0
|
||||
callback_url: Optional[str] = None
|
||||
|
||||
|
||||
class AiConclusionResponse(BaseModel):
|
||||
@@ -93,6 +119,10 @@ async def save_ai_conclusion(request: AiConclusionRequest, db: Session = Depends
|
||||
db.commit()
|
||||
logger.info(f"✅ Заключение сохранено для {request.filename}")
|
||||
|
||||
# Для внешних файлов — отправляем результат клиенту из FileAudioAPI
|
||||
if (audio.sourse or "").lower() == "external" and request.callback_url:
|
||||
_send_callback(request.callback_url, audio, conclusion_data)
|
||||
|
||||
# Отправляем webhook в Calls_WEB_Client_main для анализа
|
||||
try:
|
||||
logger.info(f"📤 Отправка webhook в Calls_WEB_Client_main для {request.filename}")
|
||||
|
||||
@@ -34,6 +34,8 @@ def query_audio_without_conclusion(db, limit=None):
|
||||
|
||||
query = db.query(Audio).filter(
|
||||
~subquery
|
||||
).filter(
|
||||
Audio.sourse == "internal"
|
||||
).order_by(Audio.index_date.asc())
|
||||
|
||||
if limit:
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File as FastAPIFile, status
|
||||
from apiApp.database import get_db
|
||||
from fastapi.responses import FileResponse
|
||||
from sqlalchemy.orm import Session
|
||||
import os, uuid
|
||||
from apiApp.config import UPLOAD_FOLDER, ALLOWED_AUDIO_EXTENSIONS, MAX_UPLOAD_SIZE
|
||||
import aiofiles
|
||||
|
||||
from apiApp.schemas import (
|
||||
AudioCreate,
|
||||
AudioResponse,
|
||||
AudioListResponse,
|
||||
MessageResponse
|
||||
)
|
||||
from apiApp.services import AudioCRUD
|
||||
|
||||
router = APIRouter(
|
||||
prefix="/external_audio",
|
||||
tags=["Внешние аудиофайлы"]
|
||||
)
|
||||
|
||||
@router.post("/upload")
|
||||
async def upload_external_audio(
|
||||
file: UploadFile = FastAPIFile(...),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Загрузка внешнего аудиофайла на сервер
|
||||
"""
|
||||
|
||||
# Проверка расширения файла
|
||||
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"
|
||||
)
|
||||
|
||||
# Сохранение файла
|
||||
file_path = UPLOAD_FOLDER / f"{uuid.uuid4()}{file_ext}"
|
||||
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)}"
|
||||
)
|
||||
|
||||
# Создание записи в БД
|
||||
try:
|
||||
audio_data = AudioCreate(filename=file.filename)
|
||||
audio = AudioCRUD.create(
|
||||
db=db,
|
||||
audio_data=audio_data,
|
||||
file_path=str(file_path),
|
||||
file_size=len(content),
|
||||
sourse="external"
|
||||
)
|
||||
return audio
|
||||
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)}"
|
||||
)
|
||||
return {"message": "External audio uploaded successfully"}
|
||||
|
||||
def send_to_recognition(file_path: str):
|
||||
"""
|
||||
Отправка аудиофайла на распознавание
|
||||
"""
|
||||
@@ -26,13 +26,20 @@ class AudioCRUD:
|
||||
return db.query(Audio).filter(Audio.filename == filename).first()
|
||||
|
||||
@staticmethod
|
||||
def create(db: Session, audio_data: AudioCreate, file_path: str, file_size: int = None) -> Audio:
|
||||
def create(
|
||||
db: Session,
|
||||
audio_data: AudioCreate,
|
||||
file_path: str,
|
||||
file_size: int = None,
|
||||
sourse: str = "internal"
|
||||
) -> Audio:
|
||||
"""Создать новую запись аудиофайла"""
|
||||
db_audio = Audio(
|
||||
filename=audio_data.filename,
|
||||
file_path=file_path,
|
||||
index_date=datetime.datetime.utcnow(),
|
||||
file_size=file_size
|
||||
file_size=file_size,
|
||||
sourse=sourse
|
||||
)
|
||||
db.add(db_audio)
|
||||
db.commit()
|
||||
|
||||
Reference in New Issue
Block a user