153 lines
5.8 KiB
Python
153 lines
5.8 KiB
Python
import logging
|
|
from typing import Literal, Optional
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
|
|
from core.exceptions import ClientDisconnectedError
|
|
from api.v1.schemas.artist import ArtistInfo, ArtistExtendedInfo, ArtistReleases, LastFmArtistEnrichment
|
|
from api.v1.schemas.discovery import SimilarArtistsResponse, TopSongsResponse, TopAlbumsResponse
|
|
from core.dependencies import get_artist_service, get_artist_discovery_service, get_artist_enrichment_service
|
|
from services.artist_service import ArtistService
|
|
from services.artist_discovery_service import ArtistDiscoveryService
|
|
from services.artist_enrichment_service import ArtistEnrichmentService
|
|
from infrastructure.validators import is_unknown_mbid
|
|
from infrastructure.msgspec_fastapi import MsgSpecRoute
|
|
from infrastructure.degradation import try_get_degradation_context
|
|
|
|
import msgspec.structs
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(route_class=MsgSpecRoute, prefix="/artists", tags=["artist"])
|
|
|
|
|
|
@router.get("/{artist_id}", response_model=ArtistInfo)
|
|
async def get_artist(
|
|
artist_id: str,
|
|
request: Request,
|
|
artist_service: ArtistService = Depends(get_artist_service)
|
|
):
|
|
if await request.is_disconnected():
|
|
raise ClientDisconnectedError("Client disconnected")
|
|
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
|
|
try:
|
|
result = await artist_service.get_artist_info(artist_id)
|
|
ctx = try_get_degradation_context()
|
|
if ctx 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 artist request"
|
|
)
|
|
|
|
|
|
@router.get("/{artist_id}/extended", response_model=ArtistExtendedInfo)
|
|
async def get_artist_extended(
|
|
artist_id: str,
|
|
artist_service: ArtistService = Depends(get_artist_service)
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
|
|
try:
|
|
return await artist_service.get_artist_extended_info(artist_id)
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid artist request"
|
|
)
|
|
|
|
|
|
@router.get("/{artist_id}/releases", response_model=ArtistReleases)
|
|
async def get_artist_releases(
|
|
artist_id: str,
|
|
offset: int = Query(default=0, ge=0),
|
|
limit: int = Query(default=50, ge=1, le=200),
|
|
artist_service: ArtistService = Depends(get_artist_service)
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
|
|
try:
|
|
return await artist_service.get_artist_releases(artist_id, offset, limit)
|
|
except ValueError as e:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid artist request"
|
|
)
|
|
|
|
|
|
@router.get("/{artist_id}/similar", response_model=SimilarArtistsResponse)
|
|
async def get_similar_artists(
|
|
artist_id: str,
|
|
count: int = Query(default=15, ge=1, le=50),
|
|
source: Literal["listenbrainz", "lastfm"] | None = Query(default=None, description="Data source: listenbrainz or lastfm"),
|
|
discovery_service: ArtistDiscoveryService = Depends(get_artist_discovery_service)
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
return await discovery_service.get_similar_artists(artist_id, count, source=source)
|
|
|
|
|
|
@router.get("/{artist_id}/top-songs", response_model=TopSongsResponse)
|
|
async def get_top_songs(
|
|
artist_id: str,
|
|
count: int = Query(default=10, ge=1, le=50),
|
|
source: Literal["listenbrainz", "lastfm"] | None = Query(default=None, description="Data source: listenbrainz or lastfm"),
|
|
discovery_service: ArtistDiscoveryService = Depends(get_artist_discovery_service)
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
return await discovery_service.get_top_songs(artist_id, count, source=source)
|
|
|
|
|
|
@router.get("/{artist_id}/top-albums", response_model=TopAlbumsResponse)
|
|
async def get_top_albums(
|
|
artist_id: str,
|
|
count: int = Query(default=10, ge=1, le=50),
|
|
source: Literal["listenbrainz", "lastfm"] | None = Query(default=None, description="Data source: listenbrainz or lastfm"),
|
|
discovery_service: ArtistDiscoveryService = Depends(get_artist_discovery_service)
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
return await discovery_service.get_top_albums(artist_id, count, source=source)
|
|
|
|
|
|
@router.get("/{artist_id}/lastfm", response_model=LastFmArtistEnrichment)
|
|
async def get_artist_lastfm_enrichment(
|
|
artist_id: str,
|
|
artist_name: str = Query(..., description="Artist name for Last.fm lookup"),
|
|
enrichment_service: ArtistEnrichmentService = Depends(get_artist_enrichment_service),
|
|
):
|
|
if is_unknown_mbid(artist_id):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=f"Invalid or unknown artist ID: {artist_id}"
|
|
)
|
|
result = await enrichment_service.get_lastfm_enrichment(artist_id, artist_name)
|
|
if result is None:
|
|
return LastFmArtistEnrichment()
|
|
return result
|