""" API endpoints для обслуживания аудиофайлов """ from fastapi import APIRouter, HTTPException, Depends from fastapi.responses import FileResponse, StreamingResponse from sqlalchemy.orm import Session from typing import Optional import os import logging from apiApp.database import get_db from apiApp.database.Audio import Audio from apiApp.config import AUDIOFILES_PATH logger = logging.getLogger(__name__) audio_files_router = APIRouter() @audio_files_router.get("/audio/{filename}") async def get_audio_file(filename: str): """ Возвращает аудиофайл для стриминга/воспроизведения Args: filename: Имя аудиофайла (например, "in-xxx.wav") Returns: StreamingResponse с аудиофайлом """ try: # Проверяем расширение файла allowed_extensions = ['.wav', '.mp3', '.ogg', '.m4a', '.flac'] if not any(filename.lower().endswith(ext) for ext in allowed_extensions): raise HTTPException( status_code=400, detail=f'Неподдерживаемый формат файла. Разрешены: {", ".join(allowed_extensions)}' ) # Формируем путь к файлу file_path = os.path.join(AUDIOFILES_PATH, filename) # Проверяем существование файла if not os.path.exists(file_path): logger.warning(f"⚠️ Файл не найден: {file_path}") raise HTTPException( status_code=404, detail=f'Файл {filename} не найден' ) logger.info(f"🎵 Отдача аудио: {filename}") # Определяем MIME тип import mimetypes mime_type, _ = mimetypes.guess_type(file_path) if mime_type is None: mime_type = 'audio/wav' # Возвращаем файл как поток def iterfile(): with open(file_path, 'rb') as f: data = f.read(8192) while data: yield data data = f.read(8192) return StreamingResponse( iterfile(), media_type=mime_type, headers={ 'Content-Disposition': f'inline; filename="{filename}"', 'Accept-Ranges': 'bytes' } ) except HTTPException: raise except Exception as e: logger.error(f"❌ Ошибка при отдаче аудио: {e}") raise HTTPException( status_code=500, detail=f'Ошибка при отдаче файла: {str(e)}' ) @audio_files_router.get("/audio/{filename}/info") async def get_audio_info(filename: str, db: Session = Depends(get_db)): """ Возвращает информацию об аудиофайле Args: filename: Имя аудиофайла Returns: JSON с информацией о файле """ try: # Ищем информацию в БД audio = db.query(Audio).filter(Audio.filename == filename).first() if not audio: raise HTTPException( status_code=404, detail=f'Файл {filename} не найден в БД' ) # Проверяем существование файла file_path = os.path.join(AUDIOFILES_PATH, filename) if not os.path.exists(file_path): raise HTTPException( status_code=404, detail=f'Файл {filename} не найден на диске' ) # Получаем размер файла file_size = os.path.getsize(file_path) # Определяем длительность (приблизительно) # В идеале использовать библиотеку типа mutagen или soundfile duration = None try: import wave with wave.open(file_path, 'r') as wav_file: frames = wav_file.getnframes() rate = wav_file.getframerate() duration = frames / float(rate) if rate > 0 else 0 except: pass return { "success": True, "filename": filename, "file_size_bytes": file_size, "duration_seconds": duration, "created_at": audio.index_date.isoformat() if audio.index_date else None, "audio_id": str(audio.id) } except HTTPException: raise except Exception as e: logger.error(f"❌ Ошибка при получении информации: {e}") raise HTTPException( status_code=500, detail=str(e) ) @audio_files_router.get("/audio/list") async def list_audio_files( db: Session = Depends(get_db), limit: int = 100, offset: int = 0 ): """ Возвращает список аудиофайлов Query Parameters: limit: Максимальное количество файлов (default: 100) offset: Смещение для пагинации (default: 0) Returns: JSON со списком файлов """ try: # Получаем список файлов из БД query = db.query(Audio).order_by(Audio.index_date.desc()) total = query.count() audio_files = query.offset(offset).limit(limit).all() files_info = [] for audio in audio_files: file_path = os.path.join(AUDIOFILES_PATH, audio.filename) exists = os.path.exists(file_path) files_info.append({ "filename": audio.filename, "file_size": audio.file_size, "created_at": audio.index_date.isoformat() if audio.index_date else None, "exists": exists, "audio_id": str(audio.id) }) return { "success": True, "total": total, "count": len(files_info), "files": files_info } except Exception as e: logger.error(f"❌ Ошибка при получении списка файлов: {e}") raise HTTPException( status_code=500, detail=str(e) ) @audio_files_router.get("/audio/{filename}/download") async def download_audio_file(filename: str): """ Возвращает аудиофайл для скачивания Args: filename: Имя аудиофайла Returns: FileResponse для скачивания """ try: # Формируем путь к файлу file_path = os.path.join(AUDIOFILES_PATH, filename) # Проверяем существование файла if not os.path.exists(file_path): raise HTTPException( status_code=404, detail=f'Файл {filename} не найден' ) logger.info(f"📥 Скачивание аудио: {filename}") return FileResponse( path=file_path, media_type='audio/wav', filename=filename ) except HTTPException: raise except Exception as e: logger.error(f"❌ Ошибка при скачивании: {e}") raise HTTPException( status_code=500, detail=str(e) )