월드컵 서비스 종료 + 응원가 MLS 확장
- 리그 탭에서 월드컵 제거, ?league=wc 는 KBO 강등, 기본 리그 kbo·mlb·mls - index.html·OG 기본 타이틀에서 월드컵 문구 제거, 월드컵 특설 정적 플레이리스트 제거 - 응원가 MLS 포함: 라인업 소스 없는 리그는 윈도우 진입 즉시 생성, 스타일은 soccer 앤섬 변형, 커버 로고는 Match flag(ESPN PNG) 재사용 - MLB 도 한국 팬 표기 지시 적용, 전날 활약 조회는 kbo/mlb 만 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
abcdf42aeb
commit
6770543318
@ -87,9 +87,9 @@ class Settings(BaseSettings):
|
||||
return "football-data"
|
||||
return self.schedule_source.lower()
|
||||
|
||||
# ── 멀티리그 (wc=월드컵 축구 · kbo · mlb · mls) ──────────
|
||||
# 활성 리그 (콤마구분). 야구·MLS 리그는 워커가 각자 소스에서 일정·결과를 동기화.
|
||||
leagues: str = "wc,kbo,mlb,mls"
|
||||
# ── 멀티리그 (kbo · mlb · mls) — 월드컵(wc)은 2026-08 종료 ──
|
||||
# 활성 리그 (콤마구분). 각 리그 워커가 자기 소스에서 일정·결과를 동기화.
|
||||
leagues: str = "kbo,mlb,mls"
|
||||
# 야구 일정 수집 윈도우 — 오늘 기준 미래 며칠치.
|
||||
# (투표 오픈·AI 예측 생성은 리그 공통: vote_open_hours_before / ai_generate_lookahead_hours)
|
||||
baseball_days_ahead: int = 7
|
||||
|
||||
@ -140,7 +140,7 @@ async def match_share(match_id: str, db: AsyncSession = Depends(get_db)) -> HTML
|
||||
tag = f"{league_ko} " if league_ko else ""
|
||||
title = f"TriplePick — {tag}{la} vs {lb} AI 승부예측"
|
||||
else:
|
||||
title = "TriplePick — AI 스포츠 승부예측 (월드컵·KBO·MLB·MLS)"
|
||||
title = "TriplePick — AI 스포츠 승부예측 (KBO·MLB·MLS)"
|
||||
desc = (
|
||||
"GPT·Claude·Gemini 3대 AI가 이 경기를 서로 다르게 예측합니다. "
|
||||
"당신의 픽을 찍고 AI와 겨뤄보세요."
|
||||
|
||||
@ -10,7 +10,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from ..config import settings
|
||||
from ..database import get_db
|
||||
from ..models import Song
|
||||
from ..models import Match, Song
|
||||
from ..services.songs import poll_generating, start_due_songs
|
||||
from .admin import require_admin
|
||||
|
||||
@ -113,6 +113,11 @@ async def song_cover(
|
||||
if len(_cover_cache) > 200:
|
||||
_cover_cache.clear()
|
||||
logo_url = _logo_url(s)
|
||||
if not logo_url and s.league == "mls":
|
||||
# MLS 로고는 Match 행의 flag(ESPN PNG URL) 재사용
|
||||
m = await db.get(Match, s.match_id)
|
||||
if m:
|
||||
logo_url = m.team_a_flag if m.team_a_code == s.team_code else m.team_b_flag
|
||||
logo = await _fetch_bytes(logo_url) if logo_url else None
|
||||
_cover_cache[key] = _compose(logo)
|
||||
return Response(
|
||||
|
||||
@ -30,14 +30,20 @@ KST = timezone(timedelta(hours=9))
|
||||
MAX_ATTEMPTS = 3
|
||||
GENERATE_TIMEOUT_MIN = 30 # Suno 작업이 이 시간 넘게 미완이면 실패 처리
|
||||
|
||||
# 곡 스타일 — 전 곡 고정 (야구장 웅장 앤섬 컨셉, 레퍼런스 확정본).
|
||||
# LLM 은 가사·제목만 쓰고 스타일은 변주하지 않는다.
|
||||
# 곡 스타일 — 전 곡 고정 (경기장 웅장 앤섬 컨셉, 레퍼런스 확정본).
|
||||
# LLM 은 가사·제목만 쓰고 스타일은 변주하지 않는다. MLS 만 soccer 로 치환.
|
||||
DEFAULT_STYLE = (
|
||||
"Korean baseball stadium cheer anthem, powerful brass fanfare, thumping "
|
||||
"drum corps, group chant call-and-response, gang vocals, energetic male "
|
||||
"crowd shouting, 128bpm, live stadium atmosphere"
|
||||
)
|
||||
|
||||
|
||||
def style_for(league: str) -> str:
|
||||
if league == "mls":
|
||||
return DEFAULT_STYLE.replace("baseball", "soccer")
|
||||
return DEFAULT_STYLE
|
||||
|
||||
# LLM 에게 주는 형식 레퍼런스 (가사 구조·스타일 문구의 톤)
|
||||
_REFERENCE = """[Intro - Brass Fanfare]
|
||||
(두! 산! 베어스!) (두! 산! 베어스!)
|
||||
@ -95,7 +101,7 @@ async def _yesterday_info(db, m: Match, code: str, name: str) -> tuple[str | Non
|
||||
"""(전날 결과 한 줄 + 활약 하이라이트, 시즌타율 맵). 경기 없으면 맵은 빈 dict."""
|
||||
line, g = await _yesterday_result(db, m, code, name)
|
||||
avg_map: dict = {}
|
||||
if g is not None:
|
||||
if g is not None and m.league in ("kbo", "mlb"):
|
||||
try:
|
||||
stats = await (
|
||||
_mlb_team_game_stats(g, code)
|
||||
@ -105,10 +111,12 @@ async def _yesterday_info(db, m: Match, code: str, name: str) -> tuple[str | Non
|
||||
except Exception as e: # noqa: BLE001
|
||||
log.warning("어제 경기 기록 조회 실패 %s: %s", g.match_id, e)
|
||||
stats = None
|
||||
if stats:
|
||||
avg_map = stats.get("avg") or {}
|
||||
if line and stats.get("highlights"):
|
||||
line += " · 활약: " + ", ".join(stats["highlights"])
|
||||
else:
|
||||
stats = None
|
||||
if stats:
|
||||
avg_map = stats.get("avg") or {}
|
||||
if line and stats.get("highlights"):
|
||||
line += " · 활약: " + ", ".join(stats["highlights"])
|
||||
return line, avg_map
|
||||
|
||||
|
||||
@ -193,10 +201,15 @@ async def _series_line(db, m: Match) -> str | None:
|
||||
|
||||
|
||||
async def fetch_lineups(m: Match) -> dict | None:
|
||||
"""오늘 선발 라인업 조회 (발표 전이면 announced=False). KBO=네이버, MLB=공식 API."""
|
||||
"""오늘 선발 라인업 조회 (발표 전이면 announced=False). KBO=네이버, MLB=공식 API.
|
||||
|
||||
MLS 는 라인업 소스 미연동 — None (라인업 없이 생성).
|
||||
"""
|
||||
if m.league == "mlb":
|
||||
return await _fetch_lineups_mlb(m)
|
||||
return await _fetch_lineups_kbo(m)
|
||||
if m.league == "kbo":
|
||||
return await _fetch_lineups_kbo(m)
|
||||
return None
|
||||
|
||||
|
||||
async def _fetch_lineups_kbo(m: Match) -> dict | None:
|
||||
@ -434,7 +447,11 @@ async def build_context(
|
||||
|
||||
|
||||
# ── LLM 작사 ───────────────────────────────────────────────────
|
||||
_LEAGUE_LABEL = {"kbo": "한국 프로야구(KBO)", "mlb": "메이저리그(MLB)"}
|
||||
_LEAGUE_LABEL = {
|
||||
"kbo": "한국 프로야구(KBO)",
|
||||
"mlb": "메이저리그(MLB)",
|
||||
"mls": "미국 프로축구(MLS)",
|
||||
}
|
||||
|
||||
|
||||
def _lyrics_prompt(team_name: str, context: str, league: str = "kbo") -> str:
|
||||
@ -442,12 +459,12 @@ def _lyrics_prompt(team_name: str, context: str, league: str = "kbo") -> str:
|
||||
f"너는 {_LEAGUE_LABEL.get(league, '프로야구')} 응원가 전문 작사가다. "
|
||||
"아래 오늘 경기 정보를 바탕으로 "
|
||||
f"'{team_name}'의 **오늘의 응원가**를 한국어로 만들어라"
|
||||
+ (" (팀명·선수명은 한국 팬에게 익숙한 표기로)" if league == "mlb" else "")
|
||||
+ (" (팀명·선수명은 한국 팬에게 익숙한 표기로)" if league in ("mlb", "mls") else "")
|
||||
+ ".\n\n"
|
||||
f"[오늘 경기 정보]\n{context}\n\n"
|
||||
"[요구사항]\n"
|
||||
"- 야구장에서 수만 관중이 떼창하는 웅장한 스타디움 앤섬\n"
|
||||
"- 오늘 경기 맥락(어제 결과 설욕/기세, 순위 싸움, 연전 차수, 선발투수)을 "
|
||||
"- 경기장에서 수만 관중이 떼창하는 웅장한 스타디움 앤섬\n"
|
||||
"- 오늘 경기 맥락(어제/최근 결과 설욕·기세, 순위 싸움, 연전 차수, 선발·라인업)을 "
|
||||
"가사에 구체적으로 녹일 것 — 선수 실명 사용 가능\n"
|
||||
"- 괄호로 관중 콜앤리스폰스 파트 표기, 섹션 태그에 연주 지시 포함\n"
|
||||
"- 분량은 1분 30초~2분 (Verse 2개 + Chorus 반복 + Bridge + Final Chorus)\n\n"
|
||||
@ -496,7 +513,9 @@ async def write_lyrics(team_name: str, context: str, league: str = "kbo") -> dic
|
||||
|
||||
|
||||
# ── 생성 시작 · 폴링 ───────────────────────────────────────────
|
||||
SONG_LEAGUES = ("kbo", "mlb") # 응원가 대상 리그 (MLS 제외)
|
||||
SONG_LEAGUES = ("kbo", "mlb", "mls") # 응원가 대상 리그
|
||||
# 라인업 발표를 기다리는 리그 — MLS 는 라인업 소스가 없어 윈도우 진입 즉시 생성
|
||||
LINEUP_LEAGUES = ("kbo", "mlb")
|
||||
|
||||
|
||||
def _song_leagues() -> list[str]:
|
||||
@ -526,7 +545,7 @@ async def _start_one(db, m: Match, side: str, lineups: dict | None) -> bool:
|
||||
|
||||
piece = await write_lyrics(name, context, m.league)
|
||||
title = str(piece.get("title") or f"{name} 오늘의 응원가").strip()[:40]
|
||||
style = DEFAULT_STYLE # 전 곡 고정 스타일 — LLM 변주 없음
|
||||
style = style_for(m.league) # 전 곡 고정 스타일 — LLM 변주 없음
|
||||
lyrics = str(piece.get("lyrics") or "").strip()
|
||||
if not lyrics:
|
||||
raise RuntimeError("LLM 가사 비어있음")
|
||||
@ -589,8 +608,14 @@ async def start_due_songs(db, force_today: bool = False) -> int:
|
||||
log.warning("lineup 조회 실패 %s: %s", m.match_id, e)
|
||||
lineups = None
|
||||
announced = bool(lineups and lineups.get("announced"))
|
||||
# 윈도우 내 라인업 대기 — 폴백 시점 전엔 발표될 때까지 생성 보류
|
||||
if not force_today and not announced and mins > settings.song_lineup_fallback_minutes_before:
|
||||
# 윈도우 내 라인업 대기 — 폴백 시점 전엔 발표될 때까지 생성 보류.
|
||||
# 라인업 소스가 없는 리그(MLS)는 윈도우 진입 즉시 생성.
|
||||
if (
|
||||
not force_today
|
||||
and m.league in LINEUP_LEAGUES
|
||||
and not announced
|
||||
and mins > settings.song_lineup_fallback_minutes_before
|
||||
):
|
||||
continue
|
||||
for side in ("a", "b"):
|
||||
try:
|
||||
|
||||
@ -13,19 +13,19 @@
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
|
||||
/>
|
||||
<title>TriplePick | AI 스포츠 승부예측 — 월드컵·KBO·MLB·MLS, GPT vs Claude vs Gemini</title>
|
||||
<title>TriplePick | AI 스포츠 승부예측 — KBO·MLB·MLS, GPT vs Claude vs Gemini</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="프로야구 승부예측(KBO)부터 해외야구 승부예측(MLB 메이저리그), 월드컵·MLS까지 — GPT·Claude·Gemini 3대 AI가 매 경기를 서로 다르게 예측합니다. 실시간 스코어·문자중계를 보며 당신의 픽을 찍고 AI와 겨뤄보세요. 무료 참여."
|
||||
content="프로야구 승부예측(KBO)부터 해외야구 승부예측(MLB 메이저리그), MLS까지 — GPT·Claude·Gemini 3대 AI가 매 경기를 서로 다르게 예측합니다. 실시간 스코어·문자중계를 보며 당신의 픽을 찍고 AI와 겨뤄보세요. 무료 참여."
|
||||
/>
|
||||
|
||||
<!-- 공유 썸네일 (Open Graph / Twitter) — 카카오톡 등 소셜 미리보기 -->
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="TriplePick" />
|
||||
<meta property="og:title" content="TriplePick — AI 스포츠 승부예측 (월드컵·KBO·MLB·MLS)" />
|
||||
<meta property="og:title" content="TriplePick — AI 스포츠 승부예측 (KBO·MLB·MLS)" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="GPT·Claude·Gemini 3대 AI가 월드컵·KBO·MLB·MLS 매 경기를 서로 다르게 예측합니다. 당신의 픽을 찍고 AI와 겨뤄보세요. 무료 참여."
|
||||
content="GPT·Claude·Gemini 3대 AI가 KBO·MLB·MLS 매 경기를 서로 다르게 예측합니다. 당신의 픽을 찍고 AI와 겨뤄보세요. 무료 참여."
|
||||
/>
|
||||
<meta property="og:url" content="https://triplepick.o2o.kr/" />
|
||||
<meta property="og:image" content="https://triplepick.o2o.kr/assets/bi/og-image.png?v=2" />
|
||||
@ -37,10 +37,10 @@
|
||||
<meta property="og:locale" content="ko_KR" />
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="TriplePick — AI 스포츠 승부예측 (월드컵·KBO·MLB·MLS)" />
|
||||
<meta name="twitter:title" content="TriplePick — AI 스포츠 승부예측 (KBO·MLB·MLS)" />
|
||||
<meta
|
||||
name="twitter:description"
|
||||
content="GPT·Claude·Gemini 3대 AI가 월드컵·KBO·MLB·MLS 매 경기를 예측합니다. 당신의 픽을 찍고 AI와 겨뤄보세요."
|
||||
content="GPT·Claude·Gemini 3대 AI가 KBO·MLB·MLS 매 경기를 예측합니다. 당신의 픽을 찍고 AI와 겨뤄보세요."
|
||||
/>
|
||||
<meta name="twitter:image" content="https://triplepick.o2o.kr/assets/bi/og-image.png?v=2" />
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
<meta name="naver-site-verification" content="d31508b6376797f7a8c587e4f60b72ffb6c7407c" />
|
||||
|
||||
<!-- 검색 키워드 / 색인 지시 -->
|
||||
<meta name="keywords" content="트리플픽, AI승부예측, 스포츠 승부예측, 프로야구 승부예측, 해외야구 승부예측, 메이저리그 승부예측, MLB 승부예측, KBO 승부예측, 야구 승부예측, 축구 승부예측, 해외축구 승부예측, MLS 승부예측, 프로야구 예측, 월드컵 승부예측, GPT 예측, 클로드 예측, 제미나이 예측, AI vs 사람, 프로야구 문자중계, TriplePick" />
|
||||
<meta name="keywords" content="트리플픽, AI승부예측, 스포츠 승부예측, 프로야구 승부예측, 해외야구 승부예측, 메이저리그 승부예측, MLB 승부예측, KBO 승부예측, 야구 승부예측, 축구 승부예측, 해외축구 승부예측, MLS 승부예측, 프로야구 예측, GPT 예측, 클로드 예측, 제미나이 예측, AI vs 사람, 프로야구 문자중계, TriplePick" />
|
||||
<meta name="robots" content="index, follow, max-image-preview:large" />
|
||||
<link rel="canonical" href="https://triplepick.o2o.kr/" />
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
"alternateName": "트리플픽",
|
||||
"url": "https://triplepick.o2o.kr/",
|
||||
"logo": "https://triplepick.o2o.kr/assets/bi/og-image.png",
|
||||
"description": "GPT·Claude·Gemini 3대 AI가 같은 경기 데이터로 승패·스코어·승리확률을 예측·비교하는 참여형 스포츠 승부예측 서비스. KBO 프로야구 승부예측·MLB 해외야구 승부예측·MLS·월드컵 지원.",
|
||||
"description": "GPT·Claude·Gemini 3대 AI가 같은 경기 데이터로 승패·스코어·승리확률을 예측·비교하는 참여형 스포츠 승부예측 서비스. KBO 프로야구 승부예측·MLB 해외야구 승부예측·MLS 지원.",
|
||||
"sameAs": [
|
||||
"https://www.youtube.com/channel/UCIiFvxaahQA-rLP8KpkDM0w",
|
||||
"https://www.instagram.com/triplepickai"
|
||||
@ -98,7 +98,7 @@
|
||||
"operatingSystem": "Web",
|
||||
"inLanguage": "ko-KR",
|
||||
"offers": { "@type": "Offer", "price": "0", "priceCurrency": "KRW" },
|
||||
"description": "GPT·Claude·Gemini의 예측을 확인하고 직접 승패와 스코어를 찍어 AI와 겨루는 무료 참여형 스포츠 승부예측 서비스. KBO 프로야구·MLB 해외야구·MLS·월드컵 경기 지원, 실시간 스코어·문자중계 제공."
|
||||
"description": "GPT·Claude·Gemini의 예측을 확인하고 직접 승패와 스코어를 찍어 AI와 겨루는 무료 참여형 스포츠 승부예측 서비스. KBO 프로야구·MLB 해외야구·MLS 경기 지원, 실시간 스코어·문자중계 제공."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -113,19 +113,19 @@
|
||||
<p>
|
||||
트리플픽(TriplePick)은 AI승부예측 서비스입니다. GPT·Claude·Gemini 3대 AI가
|
||||
같은 경기 데이터로 승패·스코어·승리확률을 서로 다르게 예측하고, 당신은 직접
|
||||
스포츠 승부예측 픽을 찍어 AI와 겨룹니다. 2026 글로벌 축구 축전(월드컵)은 물론
|
||||
KBO 프로야구·MLB·MLS 전 경기를 대상으로 무료로 참여할 수 있습니다.
|
||||
스포츠 승부예측 픽을 찍어 AI와 겨룹니다. KBO 프로야구·MLB·MLS 전 경기를
|
||||
대상으로 무료로 참여할 수 있습니다.
|
||||
</p>
|
||||
<h2>프로야구 승부예측부터 해외야구 승부예측까지</h2>
|
||||
<p>
|
||||
KBO 프로야구 승부예측은 두산·LG·KIA·삼성 등 10개 구단 전 경기를 매일
|
||||
지원하고, 해외야구 승부예측은 MLB 메이저리그 전 경기를 다룹니다. 야구뿐
|
||||
아니라 해외축구(MLS) 승부예측과 월드컵 승부예측까지 한 곳에서 — 3대 AI의
|
||||
아니라 해외축구(MLS) 승부예측까지 한 곳에서 — 3대 AI의
|
||||
예측 근거와 승리확률을 비교하고 내 픽의 적중률을 확인해 보세요.
|
||||
</p>
|
||||
<h2>트리플픽은 이런 스포츠 승부예측 서비스입니다</h2>
|
||||
<ul>
|
||||
<li>KBO 프로야구·MLB 해외야구·MLS·월드컵 4개 리그 경기별 승부예측 지원</li>
|
||||
<li>KBO 프로야구·MLB 해외야구·MLS 3개 리그 경기별 승부예측 지원</li>
|
||||
<li>3대 AI(GPT·Claude·Gemini)의 경기별 예측을 한눈에 비교</li>
|
||||
<li>승패·정확 스코어를 직접 찍는 참여형 AI승부예측</li>
|
||||
<li>진행 중 경기 실시간 스코어와 문자중계 제공</li>
|
||||
|
||||
@ -133,7 +133,6 @@ export default function Hero({
|
||||
function LeagueTabs({ lang, current }: { lang: Lang; current: League }) {
|
||||
const navigate = useNavigate();
|
||||
const tabs: { key: League; label: string }[] = [
|
||||
{ key: "wc", label: lang === "en" ? "World Cup" : "월드컵" },
|
||||
{ key: "kbo", label: "KBO" },
|
||||
{ key: "mlb", label: "MLB" },
|
||||
{ key: "mls", label: "MLS" },
|
||||
|
||||
@ -172,7 +172,7 @@ export default function MatchupHUD({ match, lang = "ko" }: { match: Match; lang?
|
||||
<div className="mt-5 grid grid-cols-[1fr_auto_1fr] items-center gap-3">
|
||||
<div className="flex flex-col items-center gap-2.5">
|
||||
<TeamFlag team={left} className={flagCls} />
|
||||
{isBaseball && (
|
||||
{isClub && (
|
||||
<CheerSongButton matchId={match.matchId} teamCode={left.code} lang={lang} />
|
||||
)}
|
||||
</div>
|
||||
@ -195,7 +195,7 @@ export default function MatchupHUD({ match, lang = "ko" }: { match: Match; lang?
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-2.5">
|
||||
<TeamFlag team={right} className={flagCls} />
|
||||
{isBaseball && (
|
||||
{isClub && (
|
||||
<CheerSongButton matchId={match.matchId} teamCode={right.code} lang={lang} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -14,23 +14,8 @@ export type Track = {
|
||||
shareText?: string;
|
||||
};
|
||||
|
||||
// 경로별 플레이리스트 — 키가 표시할 경로, 값이 해당 경로의 트랙 목록
|
||||
// VISIBLE_PATHS는 이 맵의 키에서 자동 생성됩니다.
|
||||
export const PATH_PLAYLISTS: Record<string, Track[]> = {
|
||||
"/match/A_MEX_KOR_20260619": [
|
||||
{ title: "El Tri vs 붉은 악마", artist: "aio2o", src: "/audio/Mexico vs Korea 2026.mp3", cover: "/thumbnail/Mexico vs Korea 2026.webp" },
|
||||
{ title: "Red Wave: 멕시코를 넘어", artist: "aio2o", src: "/audio/Red Wave 2026.mp3", cover: "/thumbnail/Red Wave 2026.webp" },
|
||||
{ title: "붉은 물결", artist: "aio2o", src: "/audio/붉은 물결.mp3", cover: "/thumbnail/붉은 물결.webp" },
|
||||
{ title: "Triple Pick, We Believe", artist: "aio2o", src: "/audio/Triple Pick, We Believe.mp3", cover: "/thumbnail/Triple Pick, We Believe.webp" },
|
||||
],
|
||||
"/match/A_RSA_KOR_20260625": [
|
||||
{ title: "KOR vs RSA 응원가 #1", artist: "aio2o", src: "/audio/Korea vs South Africa_ TAEGEUK FIRE_Catching.mp3", cover: "/thumbnail/kor_rsa1.webp" },
|
||||
{ title: "KOR vs RSA 응원가 #2", artist: "aio2o", src: "/audio/Korea vs South Africa_ TAEGEUK FIRE_Emotional.mp3", cover: "/thumbnail/kor_rsa2.webp" },
|
||||
{ title: "KOR vs RSA 응원가 #3", artist: "aio2o", src: "/audio/Korea vs South Africa_ TAEGEUK FIRE_Energetic.mp3", cover: "/thumbnail/kor_rsa3.webp" },
|
||||
{ title: "붉은 물결", artist: "aio2o", src: "/audio/붉은 물결.mp3", cover: "/thumbnail/붉은 물결.webp" },
|
||||
{ title: "Triple Pick, We Believe", artist: "aio2o", src: "/audio/Triple Pick, We Believe.mp3", cover: "/thumbnail/Triple Pick, We Believe.webp" },
|
||||
],
|
||||
};
|
||||
// 경로별 정적 플레이리스트 — 월드컵 종료로 비움 (구조는 특설 대비 유지)
|
||||
export const PATH_PLAYLISTS: Record<string, Track[]> = {};
|
||||
|
||||
// 두산 베어스 응원가 — 특정 경기가 아니라 두산(OB) 전 경기 공통 적용
|
||||
const DOOSAN_TRACKS: Track[] = [
|
||||
|
||||
@ -8,10 +8,9 @@ export const DEFAULT_LEAGUE: League = "kbo";
|
||||
export function useLeague(): [League, (l: League) => void] {
|
||||
const [params, setParams] = useSearchParams();
|
||||
const raw = params.get("league");
|
||||
// 월드컵(wc)은 서비스 종료 — 쿼리로 들어와도 KBO 로 강등
|
||||
const league: League =
|
||||
raw === "wc" || raw === "kbo" || raw === "mlb" || raw === "mls"
|
||||
? raw
|
||||
: DEFAULT_LEAGUE;
|
||||
raw === "kbo" || raw === "mlb" || raw === "mls" ? raw : DEFAULT_LEAGUE;
|
||||
const setLeague = (l: League) => {
|
||||
const next = new URLSearchParams(params);
|
||||
next.set("league", l);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user