Сервис для хранения файлов аудио, индексации файлов, записи и выдачи результатов распознавания
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

236 lines
7.4 KiB

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