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