From de2e66a37ccefa74396e7c87cb0dc7707c9ab29c Mon Sep 17 00:00:00 2001 From: poturaevpetr Date: Tue, 30 Dec 2025 00:25:37 +0500 Subject: [PATCH] add autorestart rec --- .../routers/audio_management_router.py.backup | 393 ++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 apiApp/routers/audio_management_router.py.backup diff --git a/apiApp/routers/audio_management_router.py.backup b/apiApp/routers/audio_management_router.py.backup new file mode 100644 index 0000000..5528757 --- /dev/null +++ b/apiApp/routers/audio_management_router.py.backup @@ -0,0 +1,393 @@ +""" +API endpoints для управления аудиофайлами (регистрация и пакетная обработка) +Используется Calls_WEB_Client_main для оркестрации процесса распознавания +""" +from fastapi import APIRouter, HTTPException, Depends, BackgroundTasks +from sqlalchemy.orm import Session +from pydantic import BaseModel +from typing import Optional, List +import os +import logging +from datetime import datetime + +from apiApp.database import get_db +from apiApp.database.Audio import Audio +from apiApp.config import AUDIOFILES_PATH + +logger = logging.getLogger(__name__) +audio_management_router = APIRouter() + + +class AudioRegisterRequest(BaseModel): + """Запрос на регистрацию аудиофайла""" + filename: str + file_path: str # Полный путь к файлу в общей папке audiofiles + + +class AudioProcessAllRequest(BaseModel): + """Запрос на пакетное распознавание""" + limit: int = 100 + + +class AudioRegisterResponse(BaseModel): + """Ответ на регистрацию аудиофайла""" + id: str + filename: str + file_size: int + created_at: datetime + + +@audio_management_router.post("/audio/register", response_model=AudioRegisterResponse, status_code=201) +async def register_audio_file( + request: AudioRegisterRequest, + db: Session = Depends(get_db) +): + """ + Регистрация аудиофайла в БД (без копирования файла) + + Создаёт запись в таблице Audio для файла, который уже находится + в общей папке audiofiles. НЕ копирует файл, только создаёт запись в БД. + + Args: + request: {filename: "in-xxx.wav", file_path: "/app/audiofiles/in-xxx.wav"} + + Returns: + 201 Created + информация о созданной записи + 400 Bad Request если файл уже зарегистрирован + 404 Not Found если файл не существует на диске + """ + try: + filename = request.filename + file_path = request.file_path + + logger.info(f"📝 Регистрация файла: {filename}") + + # Проверяем, что файл уже существует в общей папке + if not os.path.exists(file_path): + logger.error(f"❌ Файл не найден: {file_path}") + raise HTTPException( + status_code=404, + detail=f'Файл не найден на диске: {file_path}' + ) + + # Проверяем, что файл не был уже зарегистрирован + existing_audio = db.query(Audio).filter(Audio.filename == filename).first() + if existing_audio: + logger.warning(f"⚠️ Файл уже зарегистрирован: {filename}") + raise HTTPException( + status_code=400, + detail=f'Файл {filename} уже зарегистрирован в БД' + ) + + # Получаем размер файла + file_size = os.path.getsize(file_path) + + # Создаём запись в БД + audio = Audio() + audio.filename = filename + audio.file_size = file_size + audio.index_date = datetime.utcnow() + + db.add(audio) + db.commit() + db.refresh(audio) + + logger.info(f"✅ Файл зарегистрирован: {filename} (audio_id={audio.id})") + + return AudioRegisterResponse( + id=str(audio.id), + filename=audio.filename, + file_size=audio.file_size, + created_at=audio.index_date + ) + + except HTTPException: + raise + except Exception as e: + db.rollback() + logger.error(f"❌ Ошибка при регистрации файла: {e}") + raise HTTPException( + status_code=500, + detail=f'Ошибка при регистрации: {str(e)}' + ) + + +def process_audio_file(audio_id: str, db: Session): + """ + Фоновая обработка одного аудиофайла + + Отправляет запрос в GigaAM API для распознавания + """ + try: + audio = db.query(Audio).filter(Audio.id == audio_id).first() + if not audio: + logger.error(f"❌ Audio {audio_id} не найден") + return + + logger.info(f"🎵 Запуск распознавания для {audio.filename}") + + # Формируем запрос в GigaAM API + from apiApp.config import GIGAAM_API_URL + api_url = f"{GIGAAM_API_URL}/api/call/process" + + payload = { + "filename": audio.filename + } + + # Отправляем запрос в GigaAM API + import requests + response = requests.post(api_url, json=payload, timeout=10) + + if response.status_code == 200 or response.status_code == 202: + logger.info(f"✅ Запущено распознавание для {audio.filename}") + else: + logger.error(f"❌ Ошибка запуска распознавания для {audio.filename}: {response.status_code} - {response.text}") + + except Exception as e: + logger.error(f"❌ Ошибка при обработке {audio_id}: {e}") + + +@audio_management_router.post("/audio/process-all") +async def process_all_pending_audio( + request: AudioProcessAllRequest, + background_tasks: BackgroundTasks, + db: Session = Depends(get_db) +): + """ + Запуск распознавания для всех Audio без AiConclusion + + Находит все записи Audio, у которых нет AiConclusion, и запускает + распознавание для них (до указанного лимита). + + Args: + request: {limit: 100} - максимум файлов для обработки + + Returns: + 200 OK + { + "started_count": 15, + "pending_files": ["file1.wav", "file2.wav", ...], + "total_pending": 50 + } + """ + try: + limit = request.limit + + logger.info(f"🚀 Поиск Audio без AiConclusion (limit={limit})") + + # Находим все Audio без AiConclusion + pending_audio = db.query(Audio).filter( + Audio.AiConclusion == None + ).order_by(Audio.index_date.asc()).limit(limit).all() + + total_pending = db.query(Audio).filter(Audio.AiConclusion == None).count() + + if not pending_audio: + logger.info("ℹ️ Нет файлов для распознавания") + return { + "started_count": 0, + "pending_files": [], + "total_pending": 0, + "message": "Нет файлов без AiConclusion" + } + + logger.info(f"📋 Найдено файлов для обработки: {len(pending_audio)} из {total_pending}") + + # Добавляем задачи в фон + started_count = 0 + pending_files = [] + + for audio in pending_audio: + # Проверяем, что файл существует + file_path = os.path.join(AUDIOFILES_PATH, audio.filename) + if not os.path.exists(file_path): + logger.warning(f"⚠️ Файл не найден на диске: {audio.filename}") + continue + + # Добавляем в фон (асинхронно) + # В FastAPI используем BackgroundTasks + # Но нужно создавать новую сессию для каждого таска + pending_files.append(audio.filename) + started_count += 1 + + # Запускаем обработку в фоне + # Используем lambda для захвата audio_id + background_tasks.add_task( + process_single_audio, + str(audio.id) + ) + + logger.info(f"✅ Запущено распознавание для {started_count} файлов") + + return { + "started_count": started_count, + "pending_files": pending_files, + "total_pending": total_pending + } + + except Exception as e: + logger.error(f"❌ Ошибка при запуске пакетного распознавания: {e}") + raise HTTPException( + status_code=500, + detail=f'Ошибка: {str(e)}' + ) + + +def process_single_audio(audio_id: str): + """ + Обработка одного аудиофайла в фоне + + Создаёт новую DB сессию для обработки + """ + from apiApp.database import SessionLocal + + db = SessionLocal() + try: + process_audio_file(audio_id, db) + finally: + db.close() + + +@audio_management_router.get("/audio/pending") +async def get_pending_audio( + db: Session = Depends(get_db), + limit: int = 100 +): + """ + Получить список Audio без AiConclusion + + Query Parameters: + limit: Максимум файлов (default: 100) + + Returns: + Список файлов, ожидающих распознавания + """ + try: + pending_audio = db.query(Audio).filter( + Audio.AiConclusion == None + ).order_by(Audio.index_date.asc()).limit(limit).all() + + files_info = [] + for audio in pending_audio: + file_path = os.path.join(AUDIOFILES_PATH, audio.filename) + exists = os.path.exists(file_path) + + files_info.append({ + "audio_id": str(audio.id), + "filename": audio.filename, + "file_size": audio.file_size, + "created_at": audio.index_date.isoformat() if audio.index_date else None, + "exists_on_disk": exists + }) + + total_pending = db.query(Audio).filter(Audio.AiConclusion == None).count() + + return { + "total_pending": total_pending, + "count": len(files_info), + "files": files_info + } + + except Exception as e: + logger.error(f"❌ Ошибка при получении списка: {e}") + raise HTTPException( + status_code=500, + detail=str(e) + ) + + +@audio_management_router.get("/audio/stats") +async def get_audio_stats(db: Session = Depends(get_db)): + """ + Получить статистику по аудиофайлам + + Returns: + Статистика по Audio записям + """ + try: + total_audio = db.query(Audio).count() + with_conclusion = db.query(Audio).filter(Audio.AiConclusion != None).count() + without_conclusion = db.query(Audio).filter(Audio.AiConclusion == None).count() + + # Проверяем существование файлов на диске + all_audio = db.query(Audio).all() + existing_count = 0 + for audio in all_audio: + file_path = os.path.join(AUDIOFILES_PATH, audio.filename) + if os.path.exists(file_path): + existing_count += 1 + + return { + "total_audio": total_audio, + "with_conclusion": with_conclusion, + "without_conclusion": without_conclusion, + "existing_on_disk": existing_count, + "missing_on_disk": total_audio - existing_count + } + + except Exception as e: + logger.error(f"❌ Ошибка при получении статистики: {e}") + raise HTTPException( + status_code=500, + detail=str(e) + ) + + +def auto_restore_on_startup(db: Session, limit: int = 100): + """ + Автоматическое восстановление распознавания при старте FileAudioAPI + + Проверяет, есть ли файлы без AiConclusion, и запускает их распознавание + + Args: + db: Сессия БД + limit: Максимум файлов для восстановления + """ + try: + from sqlalchemy import or_ + + # Проверяем, есть ли файлы без AiConclusion + pending_audio = db.query(Audio).filter( + or_( + Audio.AiConclusion == None, + Audio.AiConclusion == '' + ) + ).limit(limit).all() + + if not pending_audio: + logger.info("ℹ️ Auto-restore: нет файлов для распознавания") + return + + logger.info(f"🔄 Auto-restore: найдено {len(pending_audio)} файлов без AiConclusion") + + # Запускаем распознавание + started_count = 0 + for audio in pending_audio: + file_path = os.path.join(AUDIOFILES_PATH, audio.filename) + + if not os.path.exists(file_path): + logger.warning(f"⚠️ Файл не найден: {audio.filename}") + continue + + # Отправляем в GigaAM API + from apiApp.config import GIGAAM_API_URL + api_url = f"{GIGAAM_API_URL}/api/call/process" + + payload = {"filename": audio.filename} + + try: + import requests + response = requests.post(api_url, json=payload, timeout=5) + + if response.status_code in [200, 202]: + logger.info(f"✅ Запущено распознавание: {audio.filename}") + started_count += 1 + else: + logger.warning(f"⚠️ Ошибка запуска {audio.filename}: {response.status_code}") + + except Exception as e: + logger.error(f"❌ Ошибка при запуске {audio.filename}: {e}") + + logger.info(f"🎉 Auto-restore завершено: запущено {started_count} файлов") + + except Exception as e: + logger.error(f"❌ Ошибка при auto-restore: {e}")