Hook up BEMAPI client support to data layer, use it to allow bootstrapping Dance Evolution from a remote server.

This commit is contained in:
Jennifer Taylor 2025-10-01 02:16:53 +00:00
parent 363cc90785
commit e6cab6fd94
4 changed files with 109 additions and 4 deletions

View File

@ -147,6 +147,7 @@ class APIClient:
GameConstants.POPN_MUSIC: "popnmusic",
GameConstants.REFLEC_BEAT: "reflecbeat",
GameConstants.SDVX: "soundvoltex",
GameConstants.DANCE_EVOLUTION: "danceevolution",
}.get(game)
if servergame is None:
raise UnsupportedRequestAPIException("The client does not support this game/version!")
@ -217,6 +218,9 @@ class APIClient:
VersionConstants.SDVX_GRAVITY_WARS: "3",
VersionConstants.SDVX_HEAVENLY_HAVEN: "4",
},
GameConstants.DANCE_EVOLUTION: {
VersionConstants.DANCE_EVOLUTION: "1",
}
}
.get(game, {})
.get(version)

View File

@ -38,6 +38,34 @@ class GlobalMusicData(BaseGlobalData):
def __max(self, int1: int, int2: int) -> int:
return max(int1, int2)
def __format_danevo_score(self, version: int, songid: int, songchart: int, data: Dict[str, Any]) -> Score:
grade = {
"AAA": DBConstants.DANEVO_GRADE_AAA,
"AA": DBConstants.DANEVO_GRADE_AA,
"A": DBConstants.DANEVO_GRADE_A,
"B": DBConstants.DANEVO_GRADE_B,
"C": DBConstants.DANEVO_GRADE_C,
"D": DBConstants.DANEVO_GRADE_D,
"E": DBConstants.DANEVO_GRADE_E,
"F": DBConstants.DANEVO_GRADE_FAILED,
}.get(data.get("grade"), DBConstants.DANEVO_GRADE_FAILED)
return Score(
-1,
songid,
songchart,
int(data.get("points", 0)),
int(data.get("timestamp", -1)),
self.__max(int(data.get("timestamp", -1)), int(data.get("updated", -1))),
-1, # No location for remote play
1, # No play info for remote play
{
"combo": int(data.get("combo", -1)),
"grade": grade,
"full_combo": bool(data.get("full_combo", False)),
},
)
def __format_ddr_score(self, version: int, songid: int, songchart: int, data: Dict[str, Any]) -> Score:
halo = {
"none": DBConstants.DDR_HALO_NONE,
@ -317,8 +345,30 @@ class GlobalMusicData(BaseGlobalData):
return self.__format_reflec_score(version, songid, songchart, data)
if game == GameConstants.SDVX:
return self.__format_sdvx_score(version, songid, songchart, data)
if game == GameConstants.DANCE_EVOLUTION:
return self.__format_danevo_score(version, songid, songchart, data)
return None
def __merge_danevo_score(self, version: int, oldscore: Score, newscore: Score) -> Score:
return Score(
-1,
oldscore.id,
oldscore.chart,
self.__max(oldscore.points, newscore.points),
self.__max(oldscore.timestamp, newscore.timestamp),
self.__max(
self.__max(oldscore.update, newscore.update),
self.__max(oldscore.timestamp, newscore.timestamp),
),
oldscore.location, # Always propagate location from local setup if possible
oldscore.plays + newscore.plays,
{
"grade": self.__max(oldscore.data["grade"], newscore.data["grade"]),
"combo": self.__max(oldscore.data["combo"], newscore.data["combo"]),
"full_combo": oldscore.data["full_combo"] or newscore.data["full_combo"],
},
)
def __merge_ddr_score(self, version: int, oldscore: Score, newscore: Score) -> Score:
return Score(
-1,
@ -513,6 +563,8 @@ class GlobalMusicData(BaseGlobalData):
return self.__merge_reflec_score(version, oldscore, newscore)
if game == GameConstants.SDVX:
return self.__merge_sdvx_score(version, oldscore, newscore)
if game == GameConstants.DANCE_EVOLUTION:
return self.__merge_danevo_score(version, oldscore, newscore)
return oldscore
@ -929,6 +981,32 @@ class GlobalMusicData(BaseGlobalData):
return retval
def __format_danevo_song(
self,
version: int,
songid: int,
songchart: int,
name: Optional[str],
artist: Optional[str],
genre: Optional[str],
data: Dict[str, Any],
) -> Song:
return Song(
game=GameConstants.DANCE_EVOLUTION,
version=version,
songid=songid,
songchart=songchart,
name=name,
artist=artist,
genre=genre,
data={
"bpm_min": int(data["bpm_min"]),
"bpm_max": int(data["bpm_max"]),
"level": int(data["level"]),
"kcal": float(data["kcal"]),
},
)
def __format_ddr_song(
self,
version: int,
@ -1168,6 +1246,8 @@ class GlobalMusicData(BaseGlobalData):
return self.__format_reflec_song(version, songid, songchart, name, artist, genre, data)
if game == GameConstants.SDVX:
return self.__format_sdvx_song(version, songid, songchart, name, artist, genre, data)
if game == GameConstants.DANCE_EVOLUTION:
return self.__format_danevo_song(version, songid, songchart, name, artist, genre, data)
return None
def get_all_songs(

View File

@ -13,6 +13,11 @@ class GlobalUserData(BaseGlobalData):
super().__init__(api)
self.user = user
def __format_danevo_profile(self, updates: Profile, profile: Profile) -> None:
area = profile.get_str("area", "")
if area:
updates["area"] = area
def __format_ddr_profile(self, updates: Profile, profile: Profile) -> None:
area = profile.get_int("area", -1)
if area != -1:
@ -86,6 +91,8 @@ class GlobalUserData(BaseGlobalData):
self.__format_reflec_profile(new, profile)
if profile.game == GameConstants.SDVX:
self.__format_sdvx_profile(new, profile)
if profile.game == GameConstants.DANCE_EVOLUTION:
self.__format_danevo_profile(new, profile)
return new

View File

@ -6162,10 +6162,24 @@ class ImportDanceEvolution(ImportBase):
return retval
def lookup(self, server: str, token: str) -> List[Dict[str, Any]]:
# TODO: We never got far enough to support DanEvo in the server, or
# specify it in BEMAPI. So this is a dead function for now, but maybe
# some year in the future I'll be able to support this.
return []
# Grab music info from remote server
music = self.remote_music(server, token)
songs = music.get_all_songs(self.game, self.version)
lut: Dict[int, Dict[str, Any]] = {}
for song in songs:
if song.id not in lut:
lut[song.id] = {
"id": song.id,
"title": song.name,
"artist": song.artist,
"level": song.data.get_int("level"),
"bpm_min": song.data.get_int("bpm_min"),
"bpm_max": song.data.get_int("bpm_max"),
"kcal": song.data.get_float("kcal"),
}
# Return the reassembled data
return [val for _, val in lut.items()]
def import_music_db(self, songs: List[Dict[str, Any]]) -> None:
for song in songs: