92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
import os
|
||
import re
|
||
from typing import Dict, Optional
|
||
|
||
class Tja:
|
||
def __init__(self, text: str):
|
||
self.text = text
|
||
self.title: Optional[str] = None
|
||
self.subtitle: Optional[str] = None
|
||
self.wave: Optional[str] = None
|
||
self.offset: Optional[float] = None
|
||
self.courses: Dict[str, Dict[str, Optional[int]]] = {}
|
||
self._parse()
|
||
|
||
def _parse(self) -> None:
|
||
lines = self.text.split("\n")
|
||
current_course: Optional[str] = None
|
||
for raw in lines:
|
||
line = raw.strip()
|
||
if not line:
|
||
continue
|
||
if ":" in line:
|
||
k, v = line.split(":", 1)
|
||
key = k.strip().upper()
|
||
val = v.strip()
|
||
if key == "TITLE":
|
||
self.title = val or None
|
||
elif key == "SUBTITLE":
|
||
self.subtitle = val or None
|
||
elif key == "WAVE":
|
||
self.wave = val or None
|
||
elif key == "OFFSET":
|
||
try:
|
||
self.offset = float(val)
|
||
except ValueError:
|
||
self.offset = None
|
||
elif key == "COURSE":
|
||
course_map = {
|
||
"EASY": "easy",
|
||
"NORMAL": "normal",
|
||
"HARD": "hard",
|
||
"ONI": "oni",
|
||
"EDIT": "ura",
|
||
"URA": "ura",
|
||
}
|
||
current_course = course_map.get(val.strip().upper())
|
||
if current_course and current_course not in self.courses:
|
||
self.courses[current_course] = {"stars": None, "branch": False}
|
||
elif key == "LEVEL" and current_course:
|
||
try:
|
||
stars = int(re.split(r"\s+", val)[0])
|
||
except ValueError:
|
||
stars = None
|
||
self.courses[current_course]["stars"] = stars
|
||
else:
|
||
if current_course and (line.startswith("BRANCHSTART") or line.startswith("#BRANCHSTART")):
|
||
self.courses[current_course]["branch"] = True
|
||
|
||
def to_mongo(self, song_id: str, created_ns: int) -> Dict:
|
||
ext = None
|
||
if self.wave:
|
||
base = os.path.basename(self.wave)
|
||
_, e = os.path.splitext(base)
|
||
if e:
|
||
ext = e.lstrip(".").lower()
|
||
if not ext:
|
||
ext = "mp3"
|
||
courses_out: Dict[str, Optional[Dict[str, Optional[int]]]] = {}
|
||
for name in ["easy", "normal", "hard", "oni", "ura"]:
|
||
courses_out[name] = self.courses.get(name) or None
|
||
return {
|
||
"id": song_id,
|
||
"type": "tja",
|
||
"title": self.title,
|
||
"subtitle": self.subtitle,
|
||
"title_lang": {"ja": self.title, "en": None, "cn": None, "tw": None, "ko": None},
|
||
"subtitle_lang": {"ja": self.subtitle, "en": None, "cn": None, "tw": None, "ko": None},
|
||
"courses": courses_out,
|
||
"enabled": False,
|
||
"category_id": None,
|
||
"music_type": ext,
|
||
# DB 的 offset 是“额外偏移”,TJA 自身的 OFFSET 会在前端解析时应用
|
||
# 为避免双重偏移,这里固定为 0
|
||
"offset": 0,
|
||
"skin_id": None,
|
||
"preview": 0,
|
||
"volume": 1.0,
|
||
"maker_id": None,
|
||
"hash": None,
|
||
"order": song_id,
|
||
"created_ns": created_ns,
|
||
} |