feat: add AniList rankings and discovery

This commit is contained in:
jxxghp
2026-07-22 12:53:58 +08:00
parent a3c90c64ca
commit fb32c59713
17 changed files with 1904 additions and 66 deletions
+169
View File
@@ -0,0 +1,169 @@
from typing import Annotated, Optional
from fastapi import APIRouter, Depends, Query
from app import schemas
from app.chain.anilist import AniListChain
from app.core.context import MediaInfo
from app.core.security import verify_token
router = APIRouter()
PageParam = Annotated[int, Query(ge=1)]
CountParam = Annotated[int, Query(ge=1, le=50)]
def _serialize_medias(medias: list[MediaInfo]) -> list[schemas.MediaInfo]:
"""
将内部媒体对象转换为 REST 响应模型。
:param medias: 统一媒体信息列表
:return: REST 媒体响应列表
"""
return [schemas.MediaInfo(**media.to_dict()) for media in medias]
@router.get(
"/trending",
summary="查询 AniList 当前趋势榜",
response_model=list[schemas.MediaInfo],
)
async def anilist_trending(
page: PageParam = 1,
count: CountParam = 20,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaInfo]:
"""查询 AniList TRENDING NOW 榜单"""
medias = await AniListChain().async_trending(page=page, count=count)
return _serialize_medias(medias)
@router.get(
"/popular-this-season",
summary="查询 AniList 本季热门榜",
response_model=list[schemas.MediaInfo],
)
async def anilist_popular_this_season(
page: PageParam = 1,
count: CountParam = 20,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaInfo]:
"""查询 AniList POPULAR THIS SEASON 榜单"""
medias = await AniListChain().async_popular_this_season(page=page, count=count)
return _serialize_medias(medias)
@router.get(
"/discover",
summary="探索 AniList 动画",
response_model=list[schemas.MediaInfo],
)
async def anilist_discover(
page: PageParam = 1,
count: CountParam = 20,
search: Optional[str] = None,
genre: Optional[str] = None,
media_format: Optional[str] = Query(None, alias="format"),
season: Optional[str] = None,
season_year: Optional[int] = None,
status: Optional[str] = None,
country: Optional[str] = None,
sort: Optional[str] = None,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaInfo]:
"""按标题、类型、风格、季度、年份、状态、地区和排序探索 AniList 动画"""
medias = await AniListChain().async_discover(
page=page,
count=count,
search=search,
genre=genre,
media_format=media_format,
season=season,
season_year=season_year,
status=status,
country=country,
sort=sort,
)
return _serialize_medias(medias)
@router.get(
"/credits/{anilist_id}",
summary="查询 AniList 配音演员",
response_model=list[schemas.MediaPerson],
)
async def anilist_credits(
anilist_id: int,
page: PageParam = 1,
count: CountParam = 20,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaPerson]:
"""查询 AniList 动画的日语配音演员"""
return await AniListChain().async_credits(
anilist_id=anilist_id, page=page, count=count
)
@router.get(
"/recommend/{anilist_id}",
summary="查询 AniList 相关推荐",
response_model=list[schemas.MediaInfo],
)
async def anilist_recommendations(
anilist_id: int,
page: PageParam = 1,
count: CountParam = 20,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaInfo]:
"""查询 AniList 动画相关推荐"""
medias = await AniListChain().async_recommendations(
anilist_id=anilist_id, page=page, count=count
)
return _serialize_medias(medias)
@router.get(
"/person/{person_id}",
summary="查询 AniList 人物详情",
response_model=schemas.MediaPerson,
)
async def anilist_person(
person_id: int,
_: schemas.TokenPayload = Depends(verify_token),
) -> Optional[schemas.MediaPerson]:
"""根据 AniList 人物 ID 查询详情"""
return await AniListChain().async_person_detail(person_id=person_id)
@router.get(
"/person/credits/{person_id}",
summary="查询 AniList 人物作品",
response_model=list[schemas.MediaInfo],
)
async def anilist_person_credits(
person_id: int,
page: PageParam = 1,
count: CountParam = 20,
_: schemas.TokenPayload = Depends(verify_token),
) -> list[schemas.MediaInfo]:
"""查询 AniList 人物参与的动画作品"""
medias = await AniListChain().async_person_credits(
person_id=person_id, page=page, count=count
)
return _serialize_medias(medias)
@router.get(
"/{anilist_id}",
summary="查询 AniList 动画详情",
response_model=schemas.MediaInfo,
)
async def anilist_info(
anilist_id: int,
_: schemas.TokenPayload = Depends(verify_token),
) -> schemas.MediaInfo:
"""根据 AniList 媒体 ID 查询动画详情"""
info = await AniListChain().async_info(anilist_id)
if not info:
return schemas.MediaInfo()
return schemas.MediaInfo(**MediaInfo(anilist_info=info).to_dict())