150 lines
5.5 KiB
Python
150 lines
5.5 KiB
Python
import logging
|
|
from typing import Optional
|
|
|
|
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, Request, status
|
|
from core.exceptions import ClientDisconnectedError
|
|
from api.v1.schemas.album import AlbumInfo, AlbumBasicInfo, AlbumTracksInfo, LastFmAlbumEnrichment
|
|
from api.v1.schemas.discovery import SimilarAlbumsResponse, MoreByArtistResponse
|
|
from core.dependencies import get_album_service, get_album_discovery_service, get_album_enrichment_service
|
|
from services.album_service import AlbumService
|
|
from services.album_discovery_service import AlbumDiscoveryService
|
|
from services.album_enrichment_service import AlbumEnrichmentService
|
|
from infrastructure.validators import is_unknown_mbid
|
|
from infrastructure.degradation import try_get_degradation_context
|
|
from infrastructure.msgspec_fastapi import MsgSpecRoute
|
|
|
|
import msgspec.structs
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(route_class=MsgSpecRoute, prefix="/albums", tags=["album"])
|
|
|
|
|
|
@router.get("/{album_id}", response_model=AlbumInfo)
|
|
async def get_album(
|
|
album_id: str,
|
|
album_service: AlbumService = Depends(get_album_service)
|
|
):
|
|
if is_unknown_mbid(album_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown album ID: {album_id}"
|
|
)
|
|
|
|
try:
|
|
result = await album_service.get_album_info(album_id)
|
|
ctx = try_get_degradation_context()
|
|
if ctx is not None and ctx.has_degradation():
|
|
result = msgspec.structs.replace(result, service_status=ctx.degraded_summary())
|
|
return result
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid album request"
|
|
)
|
|
|
|
|
|
@router.get("/{album_id}/basic", response_model=AlbumBasicInfo)
|
|
async def get_album_basic(
|
|
album_id: str,
|
|
request: Request,
|
|
background_tasks: BackgroundTasks,
|
|
album_service: AlbumService = Depends(get_album_service)
|
|
):
|
|
"""Get minimal album info for fast initial load - no tracks."""
|
|
if await request.is_disconnected():
|
|
raise ClientDisconnectedError("Client disconnected")
|
|
|
|
if is_unknown_mbid(album_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown album ID: {album_id}"
|
|
)
|
|
|
|
try:
|
|
result = await album_service.get_album_basic_info(album_id)
|
|
background_tasks.add_task(album_service.warm_full_album_cache, album_id)
|
|
return result
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid album request"
|
|
)
|
|
|
|
|
|
@router.get("/{album_id}/tracks", response_model=AlbumTracksInfo)
|
|
async def get_album_tracks(
|
|
album_id: str,
|
|
request: Request,
|
|
album_service: AlbumService = Depends(get_album_service)
|
|
):
|
|
"""Get track list and extended details - loaded asynchronously."""
|
|
if await request.is_disconnected():
|
|
raise ClientDisconnectedError("Client disconnected")
|
|
|
|
if is_unknown_mbid(album_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown album ID: {album_id}"
|
|
)
|
|
|
|
try:
|
|
return await album_service.get_album_tracks_info(album_id)
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid album request"
|
|
)
|
|
|
|
|
|
@router.get("/{album_id}/similar", response_model=SimilarAlbumsResponse)
|
|
async def get_similar_albums(
|
|
album_id: str,
|
|
artist_id: str = Query(..., description="Artist MBID for similarity lookup"),
|
|
count: int = Query(default=10, ge=1, le=30),
|
|
discovery_service: AlbumDiscoveryService = Depends(get_album_discovery_service)
|
|
):
|
|
"""Get albums from similar artists."""
|
|
if is_unknown_mbid(album_id) or is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid or unknown album/artist ID"
|
|
)
|
|
return await discovery_service.get_similar_albums(album_id, artist_id, count)
|
|
|
|
|
|
@router.get("/{album_id}/more-by-artist", response_model=MoreByArtistResponse)
|
|
async def get_more_by_artist(
|
|
album_id: str,
|
|
artist_id: str = Query(..., description="Artist MBID"),
|
|
count: int = Query(default=10, ge=1, le=30),
|
|
discovery_service: AlbumDiscoveryService = Depends(get_album_discovery_service)
|
|
):
|
|
"""Get other albums by the same artist."""
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid or unknown artist ID"
|
|
)
|
|
return await discovery_service.get_more_by_artist(artist_id, album_id, count)
|
|
|
|
|
|
@router.get("/{album_id}/lastfm", response_model=LastFmAlbumEnrichment)
|
|
async def get_album_lastfm_enrichment(
|
|
album_id: str,
|
|
artist_name: str = Query(..., description="Artist name for Last.fm lookup"),
|
|
album_name: str = Query(..., description="Album name for Last.fm lookup"),
|
|
enrichment_service: AlbumEnrichmentService = Depends(get_album_enrichment_service),
|
|
):
|
|
if is_unknown_mbid(album_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown album ID: {album_id}"
|
|
)
|
|
result = await enrichment_service.get_lastfm_enrichment(
|
|
artist_name=artist_name, album_name=album_name, album_mbid=album_id
|
|
)
|
|
if result is None:
|
|
return LastFmAlbumEnrichment()
|
|
return result
|