feat(v1-api): support api v1

This commit is contained in:
MingxuanGame
2025-08-14 09:11:53 +00:00
parent c98ea9c723
commit a8906b8194
15 changed files with 802 additions and 18 deletions

100
app/router/v1/replay.py Normal file
View File

@@ -0,0 +1,100 @@
from __future__ import annotations
import base64
from datetime import date
from typing import Literal
from app.database.counts import ReplayWatchedCount
from app.database.score import Score
from app.dependencies.database import get_db
from app.dependencies.storage import get_storage_service
from app.models.mods import int_to_mods
from app.models.score import INT_TO_MODE
from app.storage import StorageService
from .router import router
from fastapi import Depends, HTTPException, Query
from pydantic import BaseModel
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession
class ReplayModel(BaseModel):
content: str
encoding: Literal["base64"] = "base64"
@router.get(
"/get_replay",
response_model=ReplayModel,
name="获取回放文件",
description="获取指定谱面的回放文件。",
)
async def download_replay(
beatmap: int = Query(..., alias="b", description="谱面 ID"),
user: str = Query(..., alias="u", description="用户"),
ruleset_id: int | None = Query(
None, alias="m", description="Ruleset ID", ge=0, le=3
),
score_id: int | None = Query(None, alias="s", description="成绩 ID"),
type: Literal["string", "id"] | None = Query(
None, description="用户类型string 用户名称 / id 用户 ID"
),
mods: int = Query(0, description="成绩的 MOD"),
session: AsyncSession = Depends(get_db),
storage_service: StorageService = Depends(get_storage_service),
):
mods_ = int_to_mods(mods)
if score_id is not None:
score_record = await session.get(Score, score_id)
if score_record is None:
raise HTTPException(status_code=404, detail="Score not found")
else:
score_record = (
await session.exec(
select(Score).where(
Score.beatmap_id == beatmap,
Score.user_id == user
if type == "id" or user.isdigit()
else Score.user.username == user,
Score.mods == mods_,
Score.gamemode == INT_TO_MODE[ruleset_id]
if ruleset_id is not None
else True,
)
)
).first()
if score_record is None:
raise HTTPException(status_code=404, detail="Score not found")
filepath = (
f"replays/{score_record.id}_{score_record.beatmap_id}"
f"_{score_record.user_id}_lazer_replay.osr"
)
if not await storage_service.is_exists(filepath):
raise HTTPException(status_code=404, detail="Replay file not found")
replay_watched_count = (
await session.exec(
select(ReplayWatchedCount).where(
ReplayWatchedCount.user_id == score_record.user_id,
ReplayWatchedCount.year == date.today().year,
ReplayWatchedCount.month == date.today().month,
)
)
).first()
if replay_watched_count is None:
replay_watched_count = ReplayWatchedCount(
user_id=score_record.user_id,
year=date.today().year,
month=date.today().month,
)
session.add(replay_watched_count)
replay_watched_count.count += 1
await session.commit()
data = await storage_service.read_file(filepath)
return ReplayModel(
content=base64.b64encode(data).decode("utf-8"), encoding="base64"
)