Use rosu_map to parse metadata and collect beatmap background from osz

This commit is contained in:
Natsu Kagami 2024-06-20 01:52:21 +02:00 committed by Natsu Kagami
parent 64ff4b3ed8
commit 2315f40cf1
3 changed files with 45 additions and 26 deletions

1
Cargo.lock generated
View file

@ -3294,6 +3294,7 @@ dependencies = [
"rand", "rand",
"regex", "regex",
"reqwest", "reqwest",
"rosu-map",
"rosu-pp", "rosu-pp",
"rosu-v2", "rosu-v2",
"serde", "serde",

View file

@ -17,6 +17,7 @@ regex = "1.5.6"
reqwest = "0.11.10" reqwest = "0.11.10"
rosu-pp = "1.0" rosu-pp = "1.0"
rosu-v2 = { git = "https://github.com/MaxOhn/rosu-v2", rev = "d2cd3ff8417e66890f0cd8ca38bc34717a9629dd" } rosu-v2 = { git = "https://github.com/MaxOhn/rosu-v2", rev = "d2cd3ff8417e66890f0cd8ca38bc34717a9629dd" }
rosu-map = "0.1"
time = "0.3" time = "0.3"
serde = { version = "1.0.137", features = ["derive"] } serde = { version = "1.0.137", features = ["derive"] }
serenity = "0.12" serenity = "0.12"

View file

@ -1,8 +1,8 @@
use std::collections::HashMap;
use std::io::Read; use std::io::Read;
use std::sync::Arc; use std::sync::Arc;
use osuparse::MetadataSection; use rosu_map::Beatmap as BeatmapMetadata;
use rosu_pp::any::DifficultyAttributes;
use rosu_pp::Beatmap; use rosu_pp::Beatmap;
use youmubot_db_sql::{models::osu as models, Pool}; use youmubot_db_sql::{models::osu as models, Pool};
@ -13,8 +13,18 @@ use crate::{models::Mode, mods::Mods};
/// the information collected from a download/Oppai request. /// the information collected from a download/Oppai request.
#[derive(Debug)] #[derive(Debug)]
pub struct BeatmapContent { pub struct BeatmapContent {
pub metadata: MetadataSection, pub metadata: BeatmapMetadata,
pub content: Arc<Beatmap>, pub content: Arc<Beatmap>,
/// Beatmap background, if provided as part of an .osz
pub beatmap_background: Option<Arc<BeatmapBackground>>,
}
/// Beatmap background, if provided as part of an .osz
#[derive(Debug)]
pub struct BeatmapBackground {
pub filename: String,
pub content: Box<[u8]>,
} }
/// the output of "one" oppai run. /// the output of "one" oppai run.
@ -25,16 +35,6 @@ pub struct BeatmapInfo {
pub stars: f64, pub stars: f64,
} }
impl BeatmapInfo {
fn extract(beatmap: &Beatmap, attrs: DifficultyAttributes) -> Self {
BeatmapInfo {
objects: beatmap.hit_objects.len(),
max_combo: attrs.max_combo() as usize,
stars: attrs.stars(),
}
}
}
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum Accuracy { pub enum Accuracy {
ByCount(u64, u64, u64, u64), ByCount(u64, u64, u64, u64),
@ -148,14 +148,14 @@ impl BeatmapCache {
Ok(()) Ok(())
} }
fn parse_beatmap(content: impl AsRef<str>) -> Result<BeatmapContent> { fn parse_beatmap(content: impl AsRef<[u8]>) -> Result<BeatmapContent> {
let content = content.as_ref(); let content = content.as_ref();
let metadata = osuparse::parse_beatmap(content) let metadata = BeatmapMetadata::from_bytes(content)
.map_err(|e| Error::msg(format!("Cannot parse metadata: {:?}", e)))? .map_err(|e| Error::msg(format!("Cannot parse metadata: {:?}", e)))?;
.metadata;
Ok(BeatmapContent { Ok(BeatmapContent {
metadata, metadata,
content: Arc::new(Beatmap::from_bytes(content.as_bytes())?), content: Arc::new(Beatmap::from_bytes(content)?),
beatmap_background: None,
}) })
} }
@ -176,7 +176,8 @@ impl BeatmapCache {
let mut osz = zip::read::ZipArchive::new(std::io::Cursor::new(osz.as_ref()))?; let mut osz = zip::read::ZipArchive::new(std::io::Cursor::new(osz.as_ref()))?;
let osu_files = osz.file_names().map(|v| v.to_owned()).collect::<Vec<_>>(); let osu_files = osz.file_names().map(|v| v.to_owned()).collect::<Vec<_>>();
let osu_files = osu_files let mut backgrounds: HashMap<String, Option<Arc<BeatmapBackground>>> = HashMap::new();
let mut osu_files = osu_files
.into_iter() .into_iter()
.filter(|n| n.ends_with(".osu")) .filter(|n| n.ends_with(".osu"))
.filter_map(|v| { .filter_map(|v| {
@ -186,11 +187,27 @@ impl BeatmapCache {
{ {
return None; return None;
}; };
let mut content = String::new(); let mut content = Vec::<u8>::new();
v.read_to_string(&mut content).pls_ok()?; v.read_to_end(&mut content).pls_ok()?;
Self::parse_beatmap(content).pls_ok() Self::parse_beatmap(content).pls_ok()
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
for beatmap in &mut osu_files {
if beatmap.metadata.background_file != "" {
let bg = backgrounds
.entry(beatmap.metadata.background_file.clone())
.or_insert_with(|| {
let mut file = osz.by_name(&beatmap.metadata.background_file).ok()?;
let mut content = Vec::new();
file.read_to_end(&mut content).ok()?;
Some(Arc::new(BeatmapBackground {
filename: beatmap.metadata.background_file.clone(),
content: content.into_boxed_slice(),
}))
});
beatmap.beatmap_background = bg.clone();
}
}
Ok(osu_files) Ok(osu_files)
} }
@ -199,7 +216,7 @@ impl BeatmapCache {
pub async fn download_beatmap_from_url( pub async fn download_beatmap_from_url(
&self, &self,
url: impl reqwest::IntoUrl, url: impl reqwest::IntoUrl,
) -> Result<(BeatmapContent, String)> { ) -> Result<(BeatmapContent, Vec<u8>)> {
let content = self let content = self
.client .client
.borrow() .borrow()
@ -207,10 +224,10 @@ impl BeatmapCache {
.get(url) .get(url)
.send() .send()
.await? .await?
.text() .bytes()
.await?; .await?;
let bm = Self::parse_beatmap(&content)?; let bm = Self::parse_beatmap(&content)?;
Ok((bm, content)) Ok((bm, content.to_vec()))
} }
async fn download_beatmap(&self, id: u64) -> Result<BeatmapContent> { async fn download_beatmap(&self, id: u64) -> Result<BeatmapContent> {
@ -221,7 +238,7 @@ impl BeatmapCache {
let mut bc = models::CachedBeatmapContent { let mut bc = models::CachedBeatmapContent {
beatmap_id: id as i64, beatmap_id: id as i64,
cached_at: chrono::Utc::now(), cached_at: chrono::Utc::now(),
content: content.into_bytes(), content,
}; };
bc.store(&self.pool).await?; bc.store(&self.pool).await?;
Ok(bm) Ok(bm)
@ -230,7 +247,7 @@ impl BeatmapCache {
async fn get_beatmap_db(&self, id: u64) -> Result<Option<BeatmapContent>> { async fn get_beatmap_db(&self, id: u64) -> Result<Option<BeatmapContent>> {
models::CachedBeatmapContent::by_id(id as i64, &self.pool) models::CachedBeatmapContent::by_id(id as i64, &self.pool)
.await? .await?
.map(|v| Self::parse_beatmap(String::from_utf8(v.content)?)) .map(|v| Self::parse_beatmap(v.content))
.transpose() .transpose()
} }