feat: display music details
This commit is contained in:
@@ -11,4 +11,4 @@ pub fn level_name(level: u32) -> &'static str {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mod music_db;
|
mod music_db;
|
||||||
pub use music_db::{Level, MUSIC_DB, MusicInfo};
|
pub use music_db::{Level, MusicInfo, query_music, query_music_level, preload_db};
|
||||||
|
|||||||
@@ -25,6 +25,22 @@ pub struct Level {
|
|||||||
|
|
||||||
type MusicDB = FxHashMap<u32, MusicInfo>;
|
type MusicDB = FxHashMap<u32, MusicInfo>;
|
||||||
|
|
||||||
|
pub fn preload_db() {
|
||||||
|
_ = &*MUSIC_DB;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_music(music_id: &u32) -> Option<&'static MusicInfo> {
|
||||||
|
MUSIC_DB.as_ref()?.get(music_id)
|
||||||
|
}
|
||||||
|
pub fn query_music_level(music_id: u32, level: u32) -> Option<&'static Level> {
|
||||||
|
MUSIC_DB
|
||||||
|
.as_ref()?
|
||||||
|
.get(&music_id)?
|
||||||
|
.levels
|
||||||
|
.iter()
|
||||||
|
.find(|d| d.level == level)
|
||||||
|
}
|
||||||
|
|
||||||
pub static MUSIC_DB: LazyLock<Option<MusicDB>> = LazyLock::new(|| {
|
pub static MUSIC_DB: LazyLock<Option<MusicDB>> = LazyLock::new(|| {
|
||||||
let time = SystemTime::now();
|
let time = SystemTime::now();
|
||||||
info!("loading musicDB...");
|
info!("loading musicDB...");
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::helper::level_name;
|
||||||
|
use crate::helper::query_music;
|
||||||
|
use crate::helper::query_music_level;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GetUserMusicApi {
|
pub struct GetUserMusicApi {
|
||||||
@@ -31,11 +37,78 @@ pub struct UserMusicDetail {
|
|||||||
pub music_id: u32,
|
pub music_id: u32,
|
||||||
pub level: u32,
|
pub level: u32,
|
||||||
pub play_count: i64,
|
pub play_count: i64,
|
||||||
|
/// 达成率
|
||||||
pub achievement: i64,
|
pub achievement: i64,
|
||||||
|
|
||||||
|
/// Full Combo
|
||||||
|
///
|
||||||
|
/// - 0: None
|
||||||
|
/// - 1: Full Combo
|
||||||
|
/// - 2: Full Combo+
|
||||||
|
/// - 3: All Perfect
|
||||||
|
/// - 4: All Perfect+
|
||||||
pub combo_status: i64,
|
pub combo_status: i64,
|
||||||
|
|
||||||
|
/// Full Sync
|
||||||
|
///
|
||||||
|
/// - 0: None
|
||||||
|
/// - 1: FullSync
|
||||||
|
/// - 2: FullSync+
|
||||||
|
/// - 3: FullSync DX
|
||||||
|
/// - 4: Full Sync DX+
|
||||||
|
/// - 5: SYNC
|
||||||
pub sync_status: i64,
|
pub sync_status: i64,
|
||||||
|
|
||||||
pub deluxscore_max: i64,
|
pub deluxscore_max: i64,
|
||||||
pub score_rank: i64,
|
pub score_rank: i64,
|
||||||
|
|
||||||
pub ext_num1: i64,
|
pub ext_num1: i64,
|
||||||
pub ext_num2: i64,
|
pub ext_num2: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for UserMusicDetail {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(music_title) = query_music(&self.music_id).map(|i| &i.name) {
|
||||||
|
f.write_fmt(format_args!("曲目: \t{music_title}\n"))?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("难度: \t{}\n", level_name(self.level)))?;
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"达成率: \t{}.{:04}%\n",
|
||||||
|
self.achievement / 10000,
|
||||||
|
self.achievement % 10000
|
||||||
|
))?;
|
||||||
|
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"达成状态: \t{}\n",
|
||||||
|
match self.combo_status {
|
||||||
|
0 => "无",
|
||||||
|
1 => "Full Combo",
|
||||||
|
2 => "Full Combo+",
|
||||||
|
3 => "All Perfect",
|
||||||
|
4 => "All Perfect+",
|
||||||
|
_ => "未知",
|
||||||
|
}
|
||||||
|
))?;
|
||||||
|
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"同步状态: \t{}\n",
|
||||||
|
match self.sync_status {
|
||||||
|
0 => "无",
|
||||||
|
1 => "Full Sync",
|
||||||
|
2 => "Full Sync+",
|
||||||
|
3 => "Full Sync DX",
|
||||||
|
4 => "Full Sync DX+",
|
||||||
|
_ => "未知",
|
||||||
|
}
|
||||||
|
))?;
|
||||||
|
|
||||||
|
f.write_fmt(format_args!("DX 分数: \t{}\n", self.deluxscore_max))?;
|
||||||
|
|
||||||
|
if let Some(level) = query_music_level(self.music_id, self.level) {
|
||||||
|
let rating = level.dx_rating(self.achievement as _);
|
||||||
|
f.write_fmt(format_args!("DX RATING: \t{rating}"))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
helper::MUSIC_DB,
|
helper::query_music,
|
||||||
title::model::get_user_rating_api::{
|
title::model::get_user_rating_api::{
|
||||||
MusicRating, UserRating,
|
MusicRating, UserRating,
|
||||||
dxrating::{DxCalculatedEntries, DxLevelName, DxMusicRecord, DxSheetId},
|
dxrating::{DxCalculatedEntries, DxLevelName, DxMusicRecord, DxSheetId},
|
||||||
@@ -37,9 +37,7 @@ impl TryFrom<&MusicRating> for DxMusicRecord {
|
|||||||
type Error = ConversionError;
|
type Error = ConversionError;
|
||||||
|
|
||||||
fn try_from(value: &MusicRating) -> Result<Self, Self::Error> {
|
fn try_from(value: &MusicRating) -> Result<Self, Self::Error> {
|
||||||
let music_title = MUSIC_DB
|
let music_title = query_music(&value.music_id)
|
||||||
.as_ref()
|
|
||||||
.and_then(|db| db.get(&value.music_id))
|
|
||||||
.map(|info| info.name.clone())
|
.map(|info| info.name.clone())
|
||||||
.ok_or(ConversionError::MusicNotInDB)?;
|
.ok_or(ConversionError::MusicNotInDB)?;
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@ 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;
|
||||||
|
use crate::helper::query_music;
|
||||||
|
use crate::helper::query_music_level;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -150,22 +151,11 @@ impl Display for MusicRating {
|
|||||||
|
|
||||||
impl MusicRating {
|
impl MusicRating {
|
||||||
pub fn music_title(&self) -> Option<String> {
|
pub fn music_title(&self) -> Option<String> {
|
||||||
MUSIC_DB
|
Some(query_music(&self.music_id).as_ref()?.name.clone())
|
||||||
.as_ref()?
|
|
||||||
.get(&self.music_id)
|
|
||||||
.map(|music_info| music_info.name.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dx_rating(&self) -> Option<u32> {
|
pub fn dx_rating(&self) -> Option<u32> {
|
||||||
Some(
|
Some(query_music_level(self.music_id, self.level)?.dx_rating(self.achievement))
|
||||||
MUSIC_DB
|
|
||||||
.as_ref()?
|
|
||||||
.get(&self.music_id)?
|
|
||||||
.levels
|
|
||||||
.iter()
|
|
||||||
.find(|d| d.level == self.level)?
|
|
||||||
.dx_rating(self.achievement),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use spdlog::{Level, LevelFilter::MoreSevereEqual, sink::StdStreamSink, terminal_
|
|||||||
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,
|
helper::preload_db,
|
||||||
title::{
|
title::{
|
||||||
MaiVersionExt, Sdgb1_50,
|
MaiVersionExt, Sdgb1_50,
|
||||||
methods::APIMethod,
|
methods::APIMethod,
|
||||||
@@ -69,8 +69,6 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let _ = &*MUSIC_DB;
|
|
||||||
|
|
||||||
let Cli {
|
let Cli {
|
||||||
command,
|
command,
|
||||||
machine_readable,
|
machine_readable,
|
||||||
@@ -78,6 +76,7 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
let human_readable = !machine_readable;
|
let human_readable = !machine_readable;
|
||||||
|
|
||||||
let client = ClientBuilder::default().build_async().await?;
|
let client = ClientBuilder::default().build_async().await?;
|
||||||
|
preload_db();
|
||||||
|
|
||||||
// TODO: refactor via enum_dispatch
|
// TODO: refactor via enum_dispatch
|
||||||
match command {
|
match command {
|
||||||
@@ -112,8 +111,14 @@ async fn main() -> Result<(), Box<dyn snafu::Error>> {
|
|||||||
index = Some(next_index);
|
index = Some(next_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: `Display` support for MusicDetail
|
if human_readable {
|
||||||
json_display(music_detail)?;
|
for detail in music_detail {
|
||||||
|
println!("{detail}");
|
||||||
|
println!("----------");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
json_display(music_detail)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
commands::Commands::Rating { user_id, format } => {
|
commands::Commands::Rating { user_id, format } => {
|
||||||
let rating: GetUserRatingApiResp = Sdgb1_50::request(
|
let rating: GetUserRatingApiResp = Sdgb1_50::request(
|
||||||
|
|||||||
Reference in New Issue
Block a user