use async_trait::async_trait;
use dlsite_gamebox::DlsiteClient;
use dlsite_gamebox::client::search::SearchProductQuery;
use dlsite_gamebox::interface::query::SexCategory;
use crate::models::game_meta_data::GameMetadata;
use crate::providers::GameDatabaseProvider;
pub struct DLsiteProvider {
dlsite_client: DlsiteClient,
}
impl DLsiteProvider {
pub fn new() -> Self {
DLsiteProvider {
dlsite_client: DlsiteClient::default(),
}
}
}
impl Default for DLsiteProvider {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl GameDatabaseProvider for DLsiteProvider {
fn name(&self) -> &str {
"DLsite"
}
async fn search(&self, title: &str) -> Result<Vec<GameMetadata>, Box<dyn std::error::Error + Send + Sync>> {
let search_query = SearchProductQuery {
sex_category: Some(vec![SexCategory::Male]),
keyword: Some(title.to_string()),
..Default::default()
};
match self.dlsite_client.search().search_product(&search_query).await {
Ok(search_result) => {
let mut results = Vec::new();
for (index, product) in search_result.products.into_iter().enumerate() {
if index < 3 {
match self.dlsite_client.product_api().get(&product.id).await {
Ok(detailed_product) => {
eprintln!("\n=== DLsite API 详细信息 ===");
eprintln!("Product ID: {}", product.id);
eprintln!("work_name: {}", detailed_product.work_name);
eprintln!("intro: {:?}", detailed_product.intro);
eprintln!("regist_date: {:?}", detailed_product.regist_date);
eprintln!("genres count: {}", detailed_product.genres.len());
for (i, genre) in detailed_product.genres.iter().enumerate() {
eprintln!(" genre[{}]: {:?}", i, genre);
}
eprintln!("maker_name: {}", detailed_product.maker_name);
eprintln!("creators: {:?}", detailed_product.creators);
eprintln!("========================\n");
results.push(GameMetadata {
title: Some(detailed_product.work_name),
cover_url: Some(product.thumbnail_url), description: detailed_product.intro,
release_date: detailed_product.regist_date,
developer: detailed_product.creators.as_ref()
.and_then(|c| c.voice_by.as_ref())
.and_then(|v| v.first())
.map(|v| v.name.clone()),
publisher: Some(detailed_product.maker_name),
genres: if detailed_product.genres.is_empty() {
None
} else {
Some(detailed_product.genres.into_iter().map(|genre| genre.name).collect())
},
tags: None,
});
}
Err(_) => {
results.push(GameMetadata {
title: Some(product.title),
cover_url: Some(product.thumbnail_url),
description: None,
release_date: None,
developer: product.creator,
publisher: Some(product.circle_name),
genres: None,
tags: None,
});
}
}
} else {
results.push(GameMetadata {
title: Some(product.title),
cover_url: Some(product.thumbnail_url),
description: None,
release_date: None,
developer: product.creator,
publisher: Some(product.circle_name),
genres: None,
tags: None,
});
}
}
Ok(results)
}
Err(e) => Err(Box::new(e)),
}
}
async fn get_by_id(&self, id: &str) -> Result<GameMetadata, Box<dyn std::error::Error + Send + Sync>> {
match self.dlsite_client.product_api().get(id).await {
Ok(product) => {
Ok(GameMetadata {
title: Some(product.work_name),
cover_url: None,
description: product.intro,
release_date: product.regist_date,
developer: product.creators.as_ref().and_then(|c| c.voice_by.as_ref()).and_then(|v| v.first()).map(|v| v.name.clone()),
publisher: Some(product.maker_name),
genres: if product.genres.is_empty() {
None
} else {
Some(product.genres.into_iter().map(|genre| genre.name).collect())
},
tags: None,
})
}
Err(e) => Err(Box::new(e)),
}
}
fn priority(&self) -> u32 {
90 }
fn supports_game_type(&self, game_type: &str) -> bool {
matches!(game_type, "visual_novel" | "japanese_rpg" | "doujin" | "all")
}
}