-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: arcsong database importer & arcsong json exporter
- Loading branch information
Showing
9 changed files
with
259 additions
and
194 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .json import ArcsongJsonExporter | ||
|
||
__all__ = ["ArcsongJsonExporter"] |
35 changes: 35 additions & 0 deletions
35
src/arcaea_offline/external/exporters/arcsong/definitions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
from typing import List, TypedDict | ||
|
||
|
||
class ArcsongJsonDifficultyItem(TypedDict): | ||
name_en: str | ||
name_jp: str | ||
artist: str | ||
bpm: str | ||
bpm_base: float | ||
set: str | ||
set_friendly: str | ||
time: int | ||
side: int | ||
world_unlock: bool | ||
remote_download: bool | ||
bg: str | ||
date: int | ||
version: str | ||
difficulty: int | ||
rating: int | ||
note: int | ||
chart_designer: str | ||
jacket_designer: str | ||
jacket_override: bool | ||
audio_override: bool | ||
|
||
|
||
class ArcsongJsonSongItem(TypedDict): | ||
song_id: str | ||
difficulties: List[ArcsongJsonDifficultyItem] | ||
alias: List[str] | ||
|
||
|
||
class ArcsongJsonRoot(TypedDict): | ||
songs: List[ArcsongJsonSongItem] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import logging | ||
import re | ||
from typing import List, Optional | ||
|
||
from sqlalchemy import select | ||
from sqlalchemy.orm import Session | ||
|
||
from arcaea_offline.constants.enums.arcaea import ArcaeaLanguage | ||
from arcaea_offline.database.models.v5 import Difficulty, Pack, Song | ||
|
||
from .definitions import ArcsongJsonDifficultyItem, ArcsongJsonRoot, ArcsongJsonSongItem | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
class ArcsongJsonExporter: | ||
@staticmethod | ||
def craft_difficulty_item( | ||
difficulty: Difficulty, *, base_pack: Optional[Pack] | ||
) -> ArcsongJsonDifficultyItem: | ||
song = difficulty.song | ||
pack = song.pack | ||
chart_info = difficulty.chart_info | ||
|
||
song_localized_ja = next( | ||
(lo for lo in song.localized_objects if lo.lang == ArcaeaLanguage.JA), | ||
None, | ||
) | ||
difficulty_localized_ja = next( | ||
(lo for lo in difficulty.localized_objects if lo.lang == ArcaeaLanguage.JA), | ||
None, | ||
) | ||
|
||
if difficulty_localized_ja: | ||
name_jp = difficulty_localized_ja.title or "" | ||
elif song_localized_ja: | ||
name_jp = song_localized_ja.title or "" | ||
else: | ||
name_jp = "" | ||
|
||
return { | ||
"name_en": difficulty.title or song.title, | ||
"name_jp": name_jp, | ||
"artist": difficulty.artist or song.artist, | ||
"bpm": difficulty.bpm or song.bpm or "", | ||
"bpm_base": difficulty.bpm_base or song.bpm_base or 0.0, | ||
"set": song.pack_id, | ||
"set_friendly": f"{base_pack.name} - {pack.name}" | ||
if base_pack | ||
else pack.name, | ||
"time": 0, | ||
"side": song.side or 0, | ||
"world_unlock": False, | ||
"remote_download": False, | ||
"bg": difficulty.bg or song.bg or "", | ||
"date": difficulty.date or song.date or 0, | ||
"version": difficulty.version or song.version or "", | ||
"difficulty": difficulty.rating * 2 + int(difficulty.rating_plus), | ||
"rating": chart_info.constant or 0 if chart_info else 0, | ||
"note": chart_info.notes or 0 if chart_info else 0, | ||
"chart_designer": difficulty.chart_designer or "", | ||
"jacket_designer": difficulty.jacket_desginer or "", | ||
"jacket_override": difficulty.jacket_override, | ||
"audio_override": difficulty.audio_override, | ||
} | ||
|
||
@classmethod | ||
def craft(cls, session: Session) -> ArcsongJsonRoot: | ||
songs = session.scalars(select(Song)) | ||
|
||
arcsong_songs: List[ArcsongJsonSongItem] = [] | ||
for song in songs: | ||
if len(song.difficulties) == 0: | ||
continue | ||
|
||
pack = song.pack | ||
if "_append_" in pack.id: | ||
base_pack = session.scalar( | ||
select(Pack).where(Pack.id == re.sub(r"_append_.*$", "", pack.id)) | ||
) | ||
else: | ||
base_pack = None | ||
|
||
arcsong_difficulties = [] | ||
for difficulty in song.difficulties: | ||
arcsong_difficulties.append( | ||
cls.craft_difficulty_item(difficulty, base_pack=base_pack) | ||
) | ||
|
||
arcsong_songs.append( | ||
{ | ||
"song_id": song.id, | ||
"difficulties": arcsong_difficulties, | ||
"alias": [], | ||
} | ||
) | ||
|
||
return {"songs": arcsong_songs} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import sqlite3 | ||
from typing import List, overload | ||
|
||
from arcaea_offline.constants.enums.arcaea import ArcaeaRatingClass | ||
from arcaea_offline.database.models.v5 import ChartInfo | ||
|
||
|
||
class ArcsongDatabaseImporter: | ||
@classmethod | ||
@overload | ||
def parse(cls, conn: sqlite3.Connection) -> List[ChartInfo]: ... | ||
|
||
@classmethod | ||
@overload | ||
def parse(cls, conn: sqlite3.Cursor) -> List[ChartInfo]: ... | ||
|
||
@classmethod | ||
def parse(cls, conn) -> List[ChartInfo]: | ||
if isinstance(conn, sqlite3.Connection): | ||
return cls.parse(conn.cursor()) | ||
|
||
assert isinstance(conn, sqlite3.Cursor) | ||
|
||
results = [] | ||
db_results = conn.execute( | ||
"SELECT song_id, rating_class, rating, note FROM charts" | ||
) | ||
for result in db_results: | ||
results.append( | ||
ChartInfo( | ||
song_id=result[0], | ||
rating_class=ArcaeaRatingClass(result[1]), | ||
constant=result[2], | ||
notes=result[3] or None, | ||
) | ||
) | ||
|
||
return results |
Oops, something went wrong.