diff --git a/crates/dtmm/src/controller/import.rs b/crates/dtmm/src/controller/import.rs index 131d01f..522d94a 100644 --- a/crates/dtmm/src/controller/import.rs +++ b/crates/dtmm/src/controller/import.rs @@ -381,7 +381,7 @@ pub(crate) async fn import_mod(state: ActionState, info: FileInfo) -> Result Result version, + Err(err) => { + let err = Report::new(err); + tracing::warn!( + "Failed to fetch version for Nexus download. \ + Falling back to file name:\n{:?}", + err + ); + version + } + }; + + let info = NexusInfo::from(mod_info); + tracing::debug!(version, ?info); + Some((info, version)) } else { None diff --git a/lib/nexusmods/src/lib.rs b/lib/nexusmods/src/lib.rs index 145435d..0cca768 100644 --- a/lib/nexusmods/src/lib.rs +++ b/lib/nexusmods/src/lib.rs @@ -39,6 +39,8 @@ pub enum Error { Infallible(#[from] Infallible), #[error("invalid NXM URL '{}': {0}", .1.as_str())] InvalidNXM(&'static str, Url), + #[error("{0}")] + Custom(String), } pub type Result = std::result::Result; @@ -102,6 +104,28 @@ impl Api { self.send(req).await } + #[tracing::instrument(skip(self))] + pub async fn file_version(&self, id: u64, timestamp: T) -> Result + where + T: std::fmt::Debug, + OffsetDateTime: PartialEq, + { + let url = BASE_URL_GAME.join(&format!("mods/{id}/files.json"))?; + let req = self.client.get(url); + let files: FileList = self.send(req).await?; + + let Some(file) = files + .files + .into_iter() + .find(|file| file.updated_timestamp == timestamp) + else { + let err = Error::Custom("Timestamp does not match any file".into()); + return Err(err); + }; + + Ok(file.version) + } + pub fn parse_file_name>( name: S, ) -> Option<(String, u64, String, OffsetDateTime)> { diff --git a/lib/nexusmods/src/types.rs b/lib/nexusmods/src/types.rs index b0dffd5..b88911e 100644 --- a/lib/nexusmods/src/types.rs +++ b/lib/nexusmods/src/types.rs @@ -64,6 +64,35 @@ pub struct Mod { // pub contains_adult_content: bool, } +#[derive(Debug, Deserialize)] +pub struct File { + pub id: Vec, + pub uid: u64, + pub file_id: u64, + pub name: String, + pub version: String, + pub category_id: u64, + pub category_name: String, + pub is_primary: bool, + pub size: u64, + pub file_name: String, + #[serde(with = "time::serde::timestamp")] + pub updated_timestamp: OffsetDateTime, + pub mod_version: String, + pub external_virus_scan_url: String, + pub description: String, + pub size_kb: u64, + pub size_in_bytes: u64, + pub changelog_html: String, + pub content_preview_link: String, +} + +#[derive(Debug, Deserialize)] +pub struct FileList { + pub files: Vec, + // pub file_updates: Vec, +} + #[derive(Debug, Deserialize)] pub struct DownloadLink { pub name: String,