Files
sdb2-backend/app/repositories/sound.py
JSC 0fffce53b4 feat: Implement sound normalization service and API endpoints
- Added SoundNormalizerService for normalizing audio files with support for one-pass and two-pass normalization methods.
- Introduced API endpoints for normalizing all sounds and specific sounds by ID, including support for force normalization and handling of already normalized sounds.
- Created comprehensive test suite for the sound normalizer service and its API endpoints, covering various scenarios including success, errors, and edge cases.
- Refactored sound scanning service to utilize SHA-256 for file hashing instead of MD5 for improved security.
- Enhanced logging and error handling throughout the sound normalization process.
2025-07-28 09:18:18 +02:00

152 lines
5.3 KiB
Python

"""Sound repository for database operations."""
from typing import Any
from sqlalchemy import desc, func
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession
from app.core.logging import get_logger
from app.models.sound import Sound
logger = get_logger(__name__)
class SoundRepository:
"""Repository for sound operations."""
def __init__(self, session: AsyncSession) -> None:
"""Initialize the sound repository."""
self.session = session
async def get_by_id(self, sound_id: int) -> Sound | None:
"""Get a sound by ID."""
try:
statement = select(Sound).where(Sound.id == sound_id)
result = await self.session.exec(statement)
return result.first()
except Exception:
logger.exception("Failed to get sound by ID: %s", sound_id)
raise
async def get_by_filename(self, filename: str) -> Sound | None:
"""Get a sound by filename."""
try:
statement = select(Sound).where(Sound.filename == filename)
result = await self.session.exec(statement)
return result.first()
except Exception:
logger.exception("Failed to get sound by filename: %s", filename)
raise
async def get_by_hash(self, hash_value: str) -> Sound | None:
"""Get a sound by hash."""
try:
statement = select(Sound).where(Sound.hash == hash_value)
result = await self.session.exec(statement)
return result.first()
except Exception:
logger.exception("Failed to get sound by hash")
raise
async def get_by_type(self, sound_type: str) -> list[Sound]:
"""Get all sounds by type."""
try:
statement = select(Sound).where(Sound.type == sound_type)
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to get sounds by type: %s", sound_type)
raise
async def create(self, sound_data: dict[str, Any]) -> Sound:
"""Create a new sound."""
try:
sound = Sound(**sound_data)
self.session.add(sound)
await self.session.commit()
await self.session.refresh(sound)
except Exception:
await self.session.rollback()
logger.exception("Failed to create sound")
raise
else:
logger.info("Created new sound: %s", sound.name)
return sound
async def update(self, sound: Sound, update_data: dict[str, Any]) -> Sound:
"""Update a sound."""
try:
for field, value in update_data.items():
setattr(sound, field, value)
await self.session.commit()
await self.session.refresh(sound)
except Exception:
await self.session.rollback()
logger.exception("Failed to update sound")
raise
else:
logger.info("Updated sound: %s", sound.name)
return sound
async def delete(self, sound: Sound) -> None:
"""Delete a sound."""
try:
await self.session.delete(sound)
await self.session.commit()
logger.info("Deleted sound: %s", sound.name)
except Exception:
await self.session.rollback()
logger.exception("Failed to delete sound")
raise
async def search_by_name(self, query: str) -> list[Sound]:
"""Search sounds by name (case-insensitive)."""
try:
statement = select(Sound).where(
func.lower(Sound.name).like(f"%{query.lower()}%"),
)
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to search sounds by name: %s", query)
raise
async def get_popular_sounds(self, limit: int = 10) -> list[Sound]:
"""Get the most played sounds."""
try:
statement = (
select(Sound)
.order_by(desc(Sound.play_count))
.limit(limit)
)
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to get popular sounds")
raise
async def get_unnormalized_sounds(self) -> list[Sound]:
"""Get all sounds that haven't been normalized yet."""
try:
statement = select(Sound).where(Sound.is_normalized == False) # noqa: E712
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to get unnormalized sounds")
raise
async def get_unnormalized_sounds_by_type(self, sound_type: str) -> list[Sound]:
"""Get unnormalized sounds by type."""
try:
statement = select(Sound).where(
Sound.type == sound_type,
Sound.is_normalized == False, # noqa: E712
)
result = await self.session.exec(statement)
return list(result.all())
except Exception:
logger.exception("Failed to get unnormalized sounds by type: %s", sound_type)
raise