diff --git a/Cargo.lock b/Cargo.lock index d0fe733..a5c12bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3294,6 +3294,7 @@ dependencies = [ "rand", "regex", "reqwest", + "rosu-map", "rosu-pp", "rosu-v2", "serde", diff --git a/youmubot-osu/Cargo.toml b/youmubot-osu/Cargo.toml index d3b7519..b5cc9bd 100644 --- a/youmubot-osu/Cargo.toml +++ b/youmubot-osu/Cargo.toml @@ -17,6 +17,7 @@ regex = "1.5.6" reqwest = "0.11.10" rosu-pp = "1.0" rosu-v2 = { git = "https://github.com/MaxOhn/rosu-v2", rev = "d2cd3ff8417e66890f0cd8ca38bc34717a9629dd" } +rosu-map = "0.1" time = "0.3" serde = { version = "1.0.137", features = ["derive"] } serenity = "0.12" diff --git a/youmubot-osu/src/discord/oppai_cache.rs b/youmubot-osu/src/discord/oppai_cache.rs index 7e99a5e..1f0caf9 100644 --- a/youmubot-osu/src/discord/oppai_cache.rs +++ b/youmubot-osu/src/discord/oppai_cache.rs @@ -1,8 +1,8 @@ +use std::collections::HashMap; use std::io::Read; use std::sync::Arc; -use osuparse::MetadataSection; -use rosu_pp::any::DifficultyAttributes; +use rosu_map::Beatmap as BeatmapMetadata; use rosu_pp::Beatmap; 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. #[derive(Debug)] pub struct BeatmapContent { - pub metadata: MetadataSection, + pub metadata: BeatmapMetadata, pub content: Arc, + + /// Beatmap background, if provided as part of an .osz + pub beatmap_background: Option>, +} + +/// 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. @@ -25,16 +35,6 @@ pub struct BeatmapInfo { 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)] pub enum Accuracy { ByCount(u64, u64, u64, u64), @@ -148,14 +148,14 @@ impl BeatmapCache { Ok(()) } - fn parse_beatmap(content: impl AsRef) -> Result { + fn parse_beatmap(content: impl AsRef<[u8]>) -> Result { let content = content.as_ref(); - let metadata = osuparse::parse_beatmap(content) - .map_err(|e| Error::msg(format!("Cannot parse metadata: {:?}", e)))? - .metadata; + let metadata = BeatmapMetadata::from_bytes(content) + .map_err(|e| Error::msg(format!("Cannot parse metadata: {:?}", e)))?; Ok(BeatmapContent { 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 osu_files = osz.file_names().map(|v| v.to_owned()).collect::>(); - let osu_files = osu_files + let mut backgrounds: HashMap>> = HashMap::new(); + let mut osu_files = osu_files .into_iter() .filter(|n| n.ends_with(".osu")) .filter_map(|v| { @@ -186,11 +187,27 @@ impl BeatmapCache { { return None; }; - let mut content = String::new(); - v.read_to_string(&mut content).pls_ok()?; + let mut content = Vec::::new(); + v.read_to_end(&mut content).pls_ok()?; Self::parse_beatmap(content).pls_ok() }) .collect::>(); + 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) } @@ -199,7 +216,7 @@ impl BeatmapCache { pub async fn download_beatmap_from_url( &self, url: impl reqwest::IntoUrl, - ) -> Result<(BeatmapContent, String)> { + ) -> Result<(BeatmapContent, Vec)> { let content = self .client .borrow() @@ -207,10 +224,10 @@ impl BeatmapCache { .get(url) .send() .await? - .text() + .bytes() .await?; let bm = Self::parse_beatmap(&content)?; - Ok((bm, content)) + Ok((bm, content.to_vec())) } async fn download_beatmap(&self, id: u64) -> Result { @@ -221,7 +238,7 @@ impl BeatmapCache { let mut bc = models::CachedBeatmapContent { beatmap_id: id as i64, cached_at: chrono::Utc::now(), - content: content.into_bytes(), + content, }; bc.store(&self.pool).await?; Ok(bm) @@ -230,7 +247,7 @@ impl BeatmapCache { async fn get_beatmap_db(&self, id: u64) -> Result> { models::CachedBeatmapContent::by_id(id as i64, &self.pool) .await? - .map(|v| Self::parse_beatmap(String::from_utf8(v.content)?)) + .map(|v| Self::parse_beatmap(v.content)) .transpose() }