diff --git a/app/api/v1/playlists.py b/app/api/v1/playlists.py index 2f1ce6e..c86faeb 100644 --- a/app/api/v1/playlists.py +++ b/app/api/v1/playlists.py @@ -32,7 +32,7 @@ async def get_playlist_service( @router.get("/") async def get_all_playlists( - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Get all playlists from all users.""" @@ -52,7 +52,7 @@ async def get_user_playlists( @router.get("/main") async def get_main_playlist( - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Get the global main playlist.""" @@ -89,7 +89,7 @@ async def create_playlist( @router.get("/{playlist_id}") async def get_playlist( playlist_id: int, - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistResponse: """Get a specific playlist.""" @@ -130,7 +130,7 @@ async def delete_playlist( @router.get("/search/{query}") async def search_playlists( query: str, - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistResponse]: """Search all playlists by name.""" @@ -152,7 +152,7 @@ async def search_user_playlists( @router.get("/{playlist_id}/sounds") async def get_playlist_sounds( playlist_id: int, - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> list[PlaylistSoundResponse]: """Get all sounds in a playlist.""" @@ -233,7 +233,7 @@ async def unset_current_playlist( @router.get("/{playlist_id}/stats") async def get_playlist_stats( playlist_id: int, - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 playlist_service: Annotated[PlaylistService, Depends(get_playlist_service)], ) -> PlaylistStatsResponse: """Get statistics for a playlist.""" diff --git a/app/api/v1/socket.py b/app/api/v1/socket.py index 3909b45..f0d7f64 100644 --- a/app/api/v1/socket.py +++ b/app/api/v1/socket.py @@ -1,5 +1,7 @@ """Socket.IO API endpoints for WebSocket management.""" +from typing import Annotated + from fastapi import APIRouter, Depends from app.core.dependencies import get_current_user @@ -10,7 +12,9 @@ router = APIRouter(prefix="/socket", tags=["socket"]) @router.get("/status") -async def get_socket_status(current_user: User = Depends(get_current_user)): +async def get_socket_status( + current_user: Annotated[User, Depends(get_current_user)], +) -> dict[str, int | bool]: """Get current socket connection status.""" connected_users = socket_manager.get_connected_users() @@ -25,8 +29,8 @@ async def get_socket_status(current_user: User = Depends(get_current_user)): async def send_message_to_user( target_user_id: int, message: str, - current_user: User = Depends(get_current_user), -): + current_user: Annotated[User, Depends(get_current_user)], +) -> dict[str, int | bool | str]: """Send a message to a specific user via WebSocket.""" success = await socket_manager.send_to_user( str(target_user_id), @@ -48,8 +52,8 @@ async def send_message_to_user( @router.post("/broadcast") async def broadcast_message( message: str, - current_user: User = Depends(get_current_user), -): + current_user: Annotated[User, Depends(get_current_user)], +) -> dict[str, bool | str]: """Broadcast a message to all connected users.""" await socket_manager.broadcast_to_all( "broadcast_message", diff --git a/app/api/v1/sounds.py b/app/api/v1/sounds.py index 64e0ef5..2bf32cc 100644 --- a/app/api/v1/sounds.py +++ b/app/api/v1/sounds.py @@ -5,7 +5,7 @@ from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlmodel.ext.asyncio.session import AsyncSession -from app.core.database import get_db +from app.core.database import get_db, get_session_factory from app.core.dependencies import get_current_active_user_flexible from app.models.credit_action import CreditActionType from app.models.user import User @@ -43,13 +43,11 @@ async def get_extraction_service( def get_vlc_player() -> VLCPlayerService: """Get the VLC player service.""" - from app.core.database import get_session_factory return get_vlc_player_service(get_session_factory()) def get_credit_service() -> CreditService: """Get the credit service.""" - from app.core.database import get_session_factory return CreditService(get_session_factory()) @@ -76,15 +74,16 @@ async def scan_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 + else: + return { + "message": "Sound sync completed", + "results": results, + } @router.post("/scan/custom") @@ -104,10 +103,6 @@ async def scan_custom_directory( 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, @@ -118,6 +113,11 @@ async def scan_custom_directory( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to sync directory: {e!s}", ) from e + else: + return { + "message": f"Sync of directory '{directory}' completed", + "results": results, + } # NORMALIZE @@ -127,12 +127,12 @@ async def normalize_all_sounds( normalizer_service: Annotated[ SoundNormalizerService, Depends(get_sound_normalizer_service), ], - force: bool = Query( - False, description="Force normalization of already normalized sounds", - ), - one_pass: bool | None = Query( - None, description="Use one-pass normalization (overrides config)", - ), + force: Annotated[bool, Query( # noqa: FBT002 + description="Force normalization of already normalized sounds", + )] = False, + one_pass: Annotated[bool | None, Query( + description="Use one-pass normalization (overrides config)", + )] = None, ) -> dict[str, NormalizationResults | str]: """Normalize all unnormalized sounds.""" # Only allow admins to normalize sounds @@ -147,15 +147,16 @@ async def normalize_all_sounds( force=force, one_pass=one_pass, ) - return { - "message": "Sound normalization completed", - "results": results, - } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to normalize sounds: {e!s}", ) from e + else: + return { + "message": "Sound normalization completed", + "results": results, + } @router.post("/normalize/type/{sound_type}") @@ -165,12 +166,12 @@ async def normalize_sounds_by_type( normalizer_service: Annotated[ SoundNormalizerService, Depends(get_sound_normalizer_service), ], - force: bool = Query( - False, description="Force normalization of already normalized sounds", - ), - one_pass: bool | None = Query( - None, description="Use one-pass normalization (overrides config)", - ), + force: Annotated[bool, Query( # noqa: FBT002 + description="Force normalization of already normalized sounds", + )] = False, + one_pass: Annotated[bool | None, Query( + description="Use one-pass normalization (overrides config)", + )] = None, ) -> dict[str, NormalizationResults | str]: """Normalize all sounds of a specific type (SDB, TTS, EXT).""" # Only allow admins to normalize sounds @@ -194,15 +195,16 @@ async def normalize_sounds_by_type( force=force, one_pass=one_pass, ) - return { - "message": f"Normalization of {sound_type} sounds completed", - "results": results, - } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to normalize {sound_type} sounds: {e!s}", ) from e + else: + return { + "message": f"Normalization of {sound_type} sounds completed", + "results": results, + } @router.post("/normalize/{sound_id}") @@ -212,12 +214,12 @@ async def normalize_sound_by_id( normalizer_service: Annotated[ SoundNormalizerService, Depends(get_sound_normalizer_service), ], - force: bool = Query( - False, description="Force normalization of already normalized sound", - ), - one_pass: bool | None = Query( - None, description="Use one-pass normalization (overrides config)", - ), + force: Annotated[bool, Query( # noqa: FBT002 + description="Force normalization of already normalized sound", + )] = False, + one_pass: Annotated[bool | None, Query( + description="Use one-pass normalization (overrides config)", + )] = None, ) -> dict[str, str]: """Normalize a specific sound by ID.""" # Only allow admins to normalize sounds @@ -289,11 +291,6 @@ async def create_extraction( # Queue the extraction for background processing await extraction_processor.queue_extraction(extraction_info["id"]) - return { - "message": "Extraction queued successfully", - "extraction": extraction_info, - } - except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, @@ -304,6 +301,11 @@ async def create_extraction( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to create extraction: {e!s}", ) from e + else: + return { + "message": "Extraction queued successfully", + "extraction": extraction_info, + } @router.get("/extract/status") @@ -324,7 +326,7 @@ async def get_extraction_processor_status( @router.get("/extract/{extraction_id}") async def get_extraction( extraction_id: int, - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 extraction_service: Annotated[ExtractionService, Depends(get_extraction_service)], ) -> ExtractionInfo: """Get extraction information by ID.""" @@ -337,8 +339,6 @@ async def get_extraction( detail=f"Extraction {extraction_id} not found", ) - return extraction_info - except HTTPException: raise except Exception as e: @@ -346,6 +346,8 @@ async def get_extraction( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to get extraction: {e!s}", ) from e + else: + return extraction_info @router.get("/extract") @@ -363,15 +365,15 @@ async def get_user_extractions( extractions = await extraction_service.get_user_extractions(current_user.id) - return { - "extractions": extractions, - } - except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to get extractions: {e!s}", ) from e + else: + return { + "extractions": extractions, + } # VLC PLAYER @@ -403,7 +405,10 @@ async def play_sound_with_vlc( except InsufficientCreditsError as e: raise HTTPException( status_code=status.HTTP_402_PAYMENT_REQUIRED, - detail=f"Insufficient credits: {e.required} required, {e.available} available", + detail=( + f"Insufficient credits: {e.required} required, " + f"{e.available} available" + ), ) from e # Play the sound using VLC @@ -423,6 +428,14 @@ async def play_sound_with_vlc( detail="Failed to launch VLC for sound playback", ) + except HTTPException: + raise + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to play sound: {e!s}", + ) from e + else: return { "message": f"Sound '{sound.name}' is now playing via VLC", "sound_id": sound_id, @@ -431,25 +444,16 @@ async def play_sound_with_vlc( "credits_deducted": 1, } - except HTTPException: - raise - except Exception as e: - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail=f"Failed to play sound: {e!s}", - ) from e - @router.post("/vlc/stop-all") async def stop_all_vlc_instances( - current_user: Annotated[User, Depends(get_current_active_user_flexible)], + current_user: Annotated[User, Depends(get_current_active_user_flexible)], # noqa: ARG001 vlc_player: Annotated[VLCPlayerService, Depends(get_vlc_player)], ) -> dict: """Stop all running VLC instances.""" try: - result = await vlc_player.stop_all_vlc_instances() - return result + return await vlc_player.stop_all_vlc_instances() except Exception as e: raise HTTPException( diff --git a/app/repositories/base.py b/app/repositories/base.py index 2d2b1fb..fb86fe2 100644 --- a/app/repositories/base.py +++ b/app/repositories/base.py @@ -1,6 +1,6 @@ """Base repository with common CRUD operations.""" -from typing import Any, Generic, TypeVar +from typing import Any, TypeVar from sqlmodel import select from sqlmodel.ext.asyncio.session import AsyncSession @@ -13,7 +13,7 @@ ModelType = TypeVar("ModelType") logger = get_logger(__name__) -class BaseRepository(Generic[ModelType]): +class BaseRepository[ModelType]: """Base repository with common CRUD operations.""" def __init__(self, model: type[ModelType], session: AsyncSession) -> None: @@ -42,7 +42,9 @@ class BaseRepository(Generic[ModelType]): result = await self.session.exec(statement) return result.first() except Exception: - logger.exception("Failed to get %s by ID: %s", self.model.__name__, entity_id) + logger.exception( + "Failed to get %s by ID: %s", self.model.__name__, entity_id, + ) raise async def get_all( @@ -83,12 +85,17 @@ class BaseRepository(Generic[ModelType]): self.session.add(entity) await self.session.commit() await self.session.refresh(entity) - logger.info("Created new %s with ID: %s", self.model.__name__, getattr(entity, "id", "unknown")) - return entity except Exception: await self.session.rollback() logger.exception("Failed to create %s", self.model.__name__) raise + else: + logger.info( + "Created new %s with ID: %s", + self.model.__name__, + getattr(entity, "id", "unknown"), + ) + return entity async def update(self, entity: ModelType, update_data: dict[str, Any]) -> ModelType: """Update an entity. @@ -108,12 +115,17 @@ class BaseRepository(Generic[ModelType]): self.session.add(entity) await self.session.commit() await self.session.refresh(entity) - logger.info("Updated %s with ID: %s", self.model.__name__, getattr(entity, "id", "unknown")) - return entity except Exception: await self.session.rollback() logger.exception("Failed to update %s", self.model.__name__) raise + else: + logger.info( + "Updated %s with ID: %s", + self.model.__name__, + getattr(entity, "id", "unknown"), + ) + return entity async def delete(self, entity: ModelType) -> None: """Delete an entity. @@ -125,7 +137,11 @@ class BaseRepository(Generic[ModelType]): try: await self.session.delete(entity) await self.session.commit() - logger.info("Deleted %s with ID: %s", self.model.__name__, getattr(entity, "id", "unknown")) + logger.info( + "Deleted %s with ID: %s", + self.model.__name__, + getattr(entity, "id", "unknown"), + ) except Exception: await self.session.rollback() logger.exception("Failed to delete %s", self.model.__name__)