feat: add SoundRepository and SoundScannerService for audio file management

- Implemented SoundRepository for database operations related to sounds, including methods for retrieving, creating, updating, and deleting sound records.
- Developed SoundScannerService to scan directories for audio files, calculate their metadata, and synchronize with the database.
- Added support for various audio file formats and integrated ffmpeg for audio duration extraction.
- Created comprehensive tests for sound API endpoints and sound scanner service to ensure functionality and error handling.
- Updated dependencies to include ffmpeg-python for audio processing.
This commit is contained in:
JSC
2025-07-27 23:34:17 +02:00
parent cb20746f84
commit 36949a1f1c
9 changed files with 1346 additions and 1 deletions

79
app/api/v1/sounds.py Normal file
View File

@@ -0,0 +1,79 @@
"""Sound management API endpoints."""
from typing import Annotated
from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel.ext.asyncio.session import AsyncSession
from app.core.database import get_db
from app.core.dependencies import get_current_active_user_flexible
from app.models.user import User
from app.services.sound_scanner import ScanResults, SoundScannerService
router = APIRouter(prefix="/sounds", tags=["sounds"])
async def get_sound_scanner_service(
session: Annotated[AsyncSession, Depends(get_db)],
) -> SoundScannerService:
"""Get the sound scanner service."""
return SoundScannerService(session)
@router.post("/scan")
async def scan_sounds(
current_user: Annotated[User, Depends(get_current_active_user_flexible)],
scanner_service: Annotated[SoundScannerService, Depends(get_sound_scanner_service)],
) -> dict[str, ScanResults | str]:
"""Sync the soundboard directory (add/update/delete sounds)."""
# Only allow admins to scan sounds
if current_user.role not in ["admin", "superadmin"]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only administrators can sync sounds",
)
try:
results = await scanner_service.scan_soundboard_directory()
return {
"message": "Sound sync completed",
"results": results,
}
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to sync sounds: {e!s}",
) from e
@router.post("/scan/custom")
async def scan_custom_directory(
directory: str,
current_user: Annotated[User, Depends(get_current_active_user_flexible)],
scanner_service: Annotated[SoundScannerService, Depends(get_sound_scanner_service)],
sound_type: str = "SDB",
) -> dict[str, ScanResults | str]:
"""Sync a custom directory with the database (add/update/delete sounds)."""
# Only allow admins to sync sounds
if current_user.role not in ["admin", "superadmin"]:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Only administrators can sync sounds",
)
try:
results = await scanner_service.scan_directory(directory, sound_type)
return {
"message": f"Sync of directory '{directory}' completed",
"results": results,
}
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e),
) from e
except Exception as e:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Failed to sync directory: {e!s}",
) from e