- 네이버 preview fullLineUp 조회 — 양팀 타자 8명 이상이면 발표로 간주, 확정 라인업(타순·포지션 실명)을 가사 컨텍스트에 주입 - 생성 윈도우(킥오프 150분 전~) 안에서 발표까지 대기, 킥오프 40분 전까지 미발표면 라인업 없이 폴백 생성 - 라인업 없이 만든 곡은 발표 후 자동 재생성(업그레이드) — 기존 트랙을 유지한 채 새 곡 완성 시점에 교체, 실패 시 이전 곡 복귀 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
66 lines
2.1 KiB
Python
66 lines
2.1 KiB
Python
"""오늘의 응원가 API — 조회(공개) · Suno 콜백 싱크 · 관리자 수동 생성."""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from ..database import get_db
|
|
from ..models import Song
|
|
from ..services.songs import poll_generating, start_due_songs
|
|
from .admin import require_admin
|
|
|
|
router = APIRouter(prefix="/api/songs", tags=["songs"])
|
|
|
|
KST = timezone(timedelta(hours=9))
|
|
|
|
|
|
def _song_out(s: Song) -> dict:
|
|
return {
|
|
"matchId": s.match_id,
|
|
"league": s.league,
|
|
"teamCode": s.team_code,
|
|
"teamName": s.team_name,
|
|
"title": s.title,
|
|
"lyrics": s.lyrics,
|
|
"tracks": s.tracks or [],
|
|
}
|
|
|
|
|
|
@router.get("/today")
|
|
async def today_songs(league: str = "kbo", db: AsyncSession = Depends(get_db)) -> list[dict]:
|
|
today = datetime.now(KST).date()
|
|
# 업그레이드(라인업 반영 재생성) 중인 곡도 이전 트랙을 계속 서빙 — 재생 공백 없음
|
|
rows = (
|
|
await db.execute(
|
|
select(Song)
|
|
.where(
|
|
Song.league == league,
|
|
Song.date_kst == today,
|
|
Song.status.in_(("complete", "generating")),
|
|
)
|
|
.order_by(Song.match_id, Song.team_code)
|
|
)
|
|
).scalars().all()
|
|
return [_song_out(s) for s in rows if s.tracks]
|
|
|
|
|
|
@router.post("/callback")
|
|
async def suno_callback(request: Request) -> dict:
|
|
"""Suno 게이트웨이 callBackUrl 싱크대 — 완료 감지는 워커 폴링이 담당."""
|
|
try:
|
|
await request.json()
|
|
except Exception: # noqa: BLE001 — 본문 형식 무관
|
|
pass
|
|
return {"ok": True}
|
|
|
|
|
|
@router.post("/generate", dependencies=[Depends(require_admin)])
|
|
async def force_generate(db: AsyncSession = Depends(get_db)) -> dict:
|
|
"""관리자: 오늘 KBO 전 경기 응원가 즉시 생성 시작 + 폴링 1회."""
|
|
started = await start_due_songs(db, force_today=True)
|
|
done = await poll_generating(db)
|
|
return {"ok": True, "started": started, "completed": done}
|