diff --git a/app/api/v1/sounds.py b/app/api/v1/sounds.py index 770c080..aec3b6d 100644 --- a/app/api/v1/sounds.py +++ b/app/api/v1/sounds.py @@ -10,7 +10,7 @@ from app.core.dependencies import get_current_active_user_flexible from app.models.credit_action import CreditActionType from app.models.sound import Sound from app.models.user import User -from app.repositories.sound import SoundRepository +from app.repositories.sound import SoundRepository, SoundSortField, SortOrder from app.services.credit import CreditService, InsufficientCreditsError from app.services.vlc_player import VLCPlayerService, get_vlc_player_service @@ -42,10 +42,37 @@ async def get_sounds( list[str] | None, Query(description="Filter by sound types (e.g., SDB, TTS, EXT)"), ] = None, + search: Annotated[ + str | None, + Query(description="Search sounds by name"), + ] = None, + sort_by: Annotated[ + SoundSortField | None, + Query(description="Sort by field"), + ] = None, + sort_order: Annotated[ + SortOrder, + Query(description="Sort order (asc or desc)"), + ] = SortOrder.ASC, + limit: Annotated[ + int | None, + Query(description="Maximum number of results", ge=1, le=1000), + ] = None, + offset: Annotated[ + int, + Query(description="Number of results to skip", ge=0), + ] = 0, ) -> dict[str, list[Sound]]: - """Get all sounds, optionally filtered by types.""" + """Get sounds with optional search, filtering, and sorting.""" try: - sounds = await sound_repo.get_by_types(types) + sounds = await sound_repo.search_and_sort( + search_query=search, + sound_types=types, + sort_by=sort_by, + sort_order=sort_order, + limit=limit, + offset=offset, + ) except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, diff --git a/app/repositories/sound.py b/app/repositories/sound.py index 0006960..5be2404 100644 --- a/app/repositories/sound.py +++ b/app/repositories/sound.py @@ -1,5 +1,6 @@ """Sound repository for database operations.""" +from enum import Enum from sqlalchemy import func from sqlmodel import col, select from sqlmodel.ext.asyncio.session import AsyncSession @@ -11,6 +12,26 @@ from app.repositories.base import BaseRepository logger = get_logger(__name__) +class SoundSortField(str, Enum): + """Sound sort field enumeration.""" + + NAME = "name" + FILENAME = "filename" + DURATION = "duration" + SIZE = "size" + TYPE = "type" + PLAY_COUNT = "play_count" + CREATED_AT = "created_at" + UPDATED_AT = "updated_at" + + +class SortOrder(str, Enum): + """Sort order enumeration.""" + + ASC = "asc" + DESC = "desc" + + class SoundRepository(BaseRepository[Sound]): """Repository for sound operations.""" @@ -107,3 +128,53 @@ class SoundRepository(BaseRepository[Sound]): except Exception: logger.exception("Failed to get sounds by types: %s", sound_types) raise + + async def search_and_sort( + self, + search_query: str | None = None, + sound_types: list[str] | None = None, + sort_by: SoundSortField | None = None, + sort_order: SortOrder = SortOrder.ASC, + limit: int | None = None, + offset: int = 0, + ) -> list[Sound]: + """Search and sort sounds with optional filtering.""" + try: + statement = select(Sound) + + # Apply type filter + if sound_types: + statement = statement.where(col(Sound.type).in_(sound_types)) + + # Apply search filter + if search_query and search_query.strip(): + search_pattern = f"%{search_query.strip().lower()}%" + statement = statement.where( + func.lower(Sound.name).like(search_pattern) + ) + + # Apply sorting + if sort_by: + sort_column = getattr(Sound, sort_by.value) + if sort_order == SortOrder.DESC: + statement = statement.order_by(sort_column.desc()) + else: + statement = statement.order_by(sort_column.asc()) + else: + # Default sorting by name ascending + statement = statement.order_by(Sound.name.asc()) + + # Apply pagination + if offset > 0: + statement = statement.offset(offset) + if limit is not None: + statement = statement.limit(limit) + + result = await self.session.exec(statement) + return list(result.all()) + except Exception: + logger.exception( + "Failed to search and sort sounds: query=%s, types=%s, sort_by=%s, sort_order=%s", + search_query, sound_types, sort_by, sort_order + ) + raise