sync: batch file identification via hash lookup

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I85d3f1265cad1996340ac98ac9ee1f7e6a6a6964
This commit is contained in:
raf 2026-04-18 22:58:31 +03:00
commit 838ba82790
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
5 changed files with 261 additions and 1 deletions

View file

@ -403,6 +403,78 @@ impl PlatformClient for CurseForgePlatform {
Err(e) => Err(e),
}
}
async fn request_projects_from_hashes(
&self,
hashes: &[String],
_algorithm: &str,
) -> Result<Vec<Project>> {
if hashes.is_empty() {
return Ok(Vec::new());
}
let fingerprints: Vec<u32> = hashes
.iter()
.filter_map(|h| h.parse::<u32>().ok())
.collect();
if fingerprints.is_empty() {
return Ok(Vec::new());
}
#[derive(Serialize)]
struct FingerprintRequest {
fingerprints: Vec<u32>,
}
let url = format!("{CURSEFORGE_API_BASE}/fingerprints/432");
let response = self
.client
.post(&url)
.headers(self.get_headers()?)
.json(&FingerprintRequest {
fingerprints: fingerprints.clone(),
})
.send()
.await?;
if !response.status().is_success() {
return Err(PakkerError::PlatformApiError(format!(
"CurseForge batch API error: {}",
response.status()
)));
}
let response_data: serde_json::Value = response.json().await?;
let matches = response_data["data"]["exactMatches"]
.as_array()
.cloned()
.unwrap_or_default();
let mut projects = Vec::new();
let mut seen_ids = std::collections::HashSet::new();
for m in matches {
if let Some(file) = m["file"].as_object() {
if let Some(mod_id) = file["modId"].as_u64() {
let mod_id_str = mod_id.to_string();
if seen_ids.contains(&mod_id_str) {
continue;
}
seen_ids.insert(mod_id_str.clone());
if let Ok(project) =
self.request_project_with_files(&mod_id_str, &[], &[]).await
{
projects.push(project);
}
}
}
}
Ok(projects)
}
}
// CurseForge API models