import logging from typing import Literal from fastapi import APIRouter, Depends, HTTPException, Query from api.v1.schemas.local_files import ( LocalAlbumMatch, LocalAlbumSummary, LocalPaginatedResponse, LocalStorageStats, LocalTrackInfo, ) from core.dependencies import get_local_files_service from core.exceptions import ExternalServiceError from infrastructure.msgspec_fastapi import MsgSpecRoute from services.local_files_service import LocalFilesService logger = logging.getLogger(__name__) router = APIRouter(route_class=MsgSpecRoute, prefix="/local", tags=["local-files"]) @router.get("/albums", response_model=LocalPaginatedResponse) async def get_local_albums( limit: int = Query(default=50, ge=1, le=200), offset: int = Query(default=0, ge=0), sort_by: Literal["name", "date_added", "year"] = "name", sort_order: Literal["asc", "desc"] = Query(default="asc"), q: str | None = Query(default=None, min_length=1), service: LocalFilesService = Depends(get_local_files_service), ) -> LocalPaginatedResponse: try: return await service.get_albums( limit=limit, offset=offset, sort_by=sort_by, sort_order=sort_order, search_query=q ) except ExternalServiceError as e: logger.error("Failed to get local albums: %s", e) raise HTTPException(status_code=502, detail="Failed to get local albums") @router.get("/albums/match/{musicbrainz_id}", response_model=LocalAlbumMatch) async def match_local_album( musicbrainz_id: str, service: LocalFilesService = Depends(get_local_files_service), ) -> LocalAlbumMatch: try: return await service.match_album_by_mbid(musicbrainz_id) except ExternalServiceError as e: logger.error("Failed to match local album %s: %s", musicbrainz_id, e) raise HTTPException(status_code=502, detail="Failed to match local album") @router.get( "/albums/{album_id}/tracks", response_model=list[LocalTrackInfo] ) async def get_local_album_tracks( album_id: int, service: LocalFilesService = Depends(get_local_files_service), ) -> list[LocalTrackInfo]: try: return await service.get_album_tracks_by_id(album_id) except ExternalServiceError as e: logger.error("Failed to get local album tracks %d: %s", album_id, e) raise HTTPException( status_code=502, detail="Failed to get local album tracks" ) @router.get("/search", response_model=list[LocalAlbumSummary]) async def search_local( q: str = Query(min_length=1), service: LocalFilesService = Depends(get_local_files_service), ) -> list[LocalAlbumSummary]: try: return await service.search(q) except ExternalServiceError as e: logger.error("Failed to search local files: %s", e) raise HTTPException( status_code=502, detail="Failed to search local files" ) @router.get("/recent", response_model=list[LocalAlbumSummary]) async def get_local_recent( limit: int = Query(default=20, ge=1, le=50), service: LocalFilesService = Depends(get_local_files_service), ) -> list[LocalAlbumSummary]: try: return await service.get_recently_added(limit=limit) except ExternalServiceError as e: logger.error("Failed to get recent local albums: %s", e) raise HTTPException( status_code=502, detail="Failed to get recent local albums" ) @router.get("/stats", response_model=LocalStorageStats) async def get_local_stats( service: LocalFilesService = Depends(get_local_files_service), ) -> LocalStorageStats: return await service.get_storage_stats()