Browse Source

загрузка и распознвание внешний файлов

dev
poturaevpetr 2 weeks ago
parent
commit
9fc60e8d19
  1. 4
      apiApp/database/Audio.py
  2. 30
      apiApp/routers/ai_conclusion_router.py
  3. 2
      apiApp/routers/audio_management_router.py
  4. 81
      apiApp/routers/external_audio.py
  5. 11
      apiApp/services/audio_service.py

4
apiApp/database/Audio.py

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

30
apiApp/routers/ai_conclusion_router.py

@ -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}")

2
apiApp/routers/audio_management_router.py

@ -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:

81
apiApp/routers/external_audio.py

@ -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):
"""
Отправка аудиофайла на распознавание
"""

11
apiApp/services/audio_service.py

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

Loading…
Cancel
Save