feat: simple musicDB for title
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1459,6 +1459,7 @@ dependencies = [
|
|||||||
"hmac-sha256",
|
"hmac-sha256",
|
||||||
"md5",
|
"md5",
|
||||||
"nyquest",
|
"nyquest",
|
||||||
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"snafu",
|
"snafu",
|
||||||
|
|||||||
@@ -43,3 +43,4 @@ bincode = { version = "2.0.1", optional = true }
|
|||||||
|
|
||||||
# magic macro
|
# magic macro
|
||||||
crabtime = { git = "https://github.com/wdanilo/crabtime.git", rev = "2ed856f5" }
|
crabtime = { git = "https://github.com/wdanilo/crabtime.git", rev = "2ed856f5" }
|
||||||
|
rustc-hash = "2.1.1"
|
||||||
|
|||||||
@@ -10,6 +10,5 @@ pub fn level_name(level: u32) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: MusicDB lazy load
|
mod music_db;
|
||||||
// struct MusicDB;
|
pub use music_db::{MUSIC_DB, MusicInfo,};
|
||||||
// static MUSIC_DB: LazyLock<MusicDB> = LazyLock::new(|| unimplemented!());
|
|
||||||
|
|||||||
32
sdgb-api/src/helper/music_db/mod.rs
Normal file
32
sdgb-api/src/helper/music_db/mod.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
use std::{fs::OpenOptions, sync::LazyLock};
|
||||||
|
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use spdlog::{info, warn};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct MusicInfo {
|
||||||
|
pub id: u32,
|
||||||
|
pub name: String,
|
||||||
|
pub version: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
type MusicDB = FxHashMap<u32, MusicInfo>;
|
||||||
|
|
||||||
|
pub static MUSIC_DB: LazyLock<Option<MusicDB>> = LazyLock::new(|| {
|
||||||
|
info!("loading musicDB...");
|
||||||
|
|
||||||
|
let json = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.create(false)
|
||||||
|
.open("musicDB.json")
|
||||||
|
.inspect_err(|e| warn!("failed to load musicDB: {e}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let db: Vec<MusicInfo> = serde_json::from_reader(json)
|
||||||
|
.inspect_err(|e| warn!("failed to load musicDB: {e}"))
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some(db.into_iter().map(|entry| (entry.id, entry)).collect())
|
||||||
|
});
|
||||||
@@ -3,6 +3,7 @@ use std::fmt::Display;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::helper::MUSIC_DB;
|
||||||
use crate::helper::level_name;
|
use crate::helper::level_name;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
@@ -109,6 +110,10 @@ impl Display for GetUserRatingApiResp {
|
|||||||
impl Display for MusicRating {
|
impl Display for MusicRating {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("歌曲ID: \t{}\n", self.music_id))?;
|
f.write_fmt(format_args!("歌曲ID: \t{}\n", self.music_id))?;
|
||||||
|
if let Some(info) = MUSIC_DB.as_ref().map(|db| db.get(&self.music_id)).flatten() {
|
||||||
|
let title = &info.name;
|
||||||
|
f.write_fmt(format_args!("曲目标题: \t{title}\n"))?;
|
||||||
|
}
|
||||||
f.write_fmt(format_args!(
|
f.write_fmt(format_args!(
|
||||||
"谱面版本: \t{}\n",
|
"谱面版本: \t{}\n",
|
||||||
match (self.music_id / 10000) % 10 {
|
match (self.music_id / 10000) % 10 {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use spdlog::{Level, LevelFilter::MoreSevereEqual};
|
|||||||
use sdgb_api::{
|
use sdgb_api::{
|
||||||
all_net::QRCode,
|
all_net::QRCode,
|
||||||
auth_lite::{SDGB, SDHJ, delivery_raw},
|
auth_lite::{SDGB, SDHJ, delivery_raw},
|
||||||
|
helper::MUSIC_DB,
|
||||||
title::{
|
title::{
|
||||||
MaiVersionExt, Sdgb1_40, Sdgb1_50,
|
MaiVersionExt, Sdgb1_40, Sdgb1_50,
|
||||||
methods::APIMethod,
|
methods::APIMethod,
|
||||||
@@ -52,6 +53,8 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
let _ = &*MUSIC_DB;
|
||||||
|
|
||||||
let Cli {
|
let Cli {
|
||||||
command,
|
command,
|
||||||
machine_readable,
|
machine_readable,
|
||||||
|
|||||||
77
utils/music_db_dump.py
Normal file
77
utils/music_db_dump.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# forked from maimaiDX-Api
|
||||||
|
import json
|
||||||
|
import xml.dom.minidom as minidom
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ONLY_REMOVED = True
|
||||||
|
|
||||||
|
|
||||||
|
def makeMusicDBJson():
|
||||||
|
"""
|
||||||
|
从 HDD 的文件来生成 music_db.json
|
||||||
|
推荐的是如果要国服用 那就用国际服的文件来生成
|
||||||
|
免得国服每次更新还要重新生成太麻烦
|
||||||
|
"""
|
||||||
|
# 记得改
|
||||||
|
A000_DIR = Path(
|
||||||
|
"C:/MaimaiDX/SDEZ-1.56-B/Standard/Package/Sinmai_Data/StreamingAssets/A000"
|
||||||
|
)
|
||||||
|
OPTION_DIR = Path("C:/MaimaiDX/SDGA-1.50-G/NoMovieData/StreamingAssets")
|
||||||
|
|
||||||
|
music_db: list[dict[str, str | int]] = []
|
||||||
|
DEST_PATH = Path("./musicDB.json")
|
||||||
|
|
||||||
|
dup_count = 0
|
||||||
|
music_ids = set()
|
||||||
|
|
||||||
|
music_folders = [f for f in (A000_DIR / "music").iterdir() if f.is_dir()]
|
||||||
|
for option_dir in OPTION_DIR.iterdir():
|
||||||
|
# only removed ones
|
||||||
|
if ONLY_REMOVED and option_dir.name != "A100":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (option_dir / "music").exists():
|
||||||
|
music_folders.extend(
|
||||||
|
[f for f in (option_dir / "music").iterdir() if f.is_dir()]
|
||||||
|
)
|
||||||
|
|
||||||
|
for folder in music_folders:
|
||||||
|
xml_path = folder / "Music.xml"
|
||||||
|
if xml_path.exists():
|
||||||
|
xml = minidom.parse(xml_path.as_posix())
|
||||||
|
data = xml.getElementsByTagName("MusicData")[0]
|
||||||
|
music_id = int(
|
||||||
|
data.getElementsByTagName("name")[0]
|
||||||
|
.getElementsByTagName("id")[0]
|
||||||
|
.firstChild.data
|
||||||
|
)
|
||||||
|
music_name = (
|
||||||
|
data.getElementsByTagName("name")[0]
|
||||||
|
.getElementsByTagName("str")[0]
|
||||||
|
.firstChild.data
|
||||||
|
)
|
||||||
|
music_version = (
|
||||||
|
data.getElementsByTagName("AddVersion")[0]
|
||||||
|
.getElementsByTagName("id")[0]
|
||||||
|
.firstChild.data
|
||||||
|
)
|
||||||
|
|
||||||
|
if music_id not in music_ids:
|
||||||
|
music_ids.add(music_id)
|
||||||
|
music_db.append(
|
||||||
|
{"id": music_id, "name": music_name, "version": int(music_version)}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# e.g. SDEZ-only song
|
||||||
|
dup_count += 1
|
||||||
|
|
||||||
|
print(f"Found {len(music_db)} music data")
|
||||||
|
print(f"Found {dup_count} duplications")
|
||||||
|
|
||||||
|
with open(DEST_PATH, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(music_db, f, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
makeMusicDBJson()
|
||||||
|
print("Done.")
|
||||||
Reference in New Issue
Block a user