diff --git a/Cargo.lock b/Cargo.lock index 751c80c..89d08ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,15 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "http" version = "0.2.4" @@ -307,6 +316,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.3.1" @@ -340,6 +358,15 @@ version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -456,6 +483,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.8.0" @@ -495,6 +532,31 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -658,6 +720,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "security-framework" version = "2.4.2" @@ -724,12 +792,27 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "socket2" version = "0.4.1" @@ -744,10 +827,12 @@ dependencies = [ name = "steam_shortcuts_sync" version = "0.1.0" dependencies = [ + "reqwest", "serde", "serde_json", "steam_shortcuts_util", "steamgriddb_api", + "tokio", ] [[package]] @@ -763,8 +848,6 @@ dependencies = [ [[package]] name = "steamgriddb_api" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b34454e1f8e7b229d7e7b5709d3aba0ad3c236dc8944b46013afaa649c55a1c" dependencies = [ "reqwest", "serde", @@ -829,10 +912,26 @@ dependencies = [ "libc", "memchr", "mio", + "num_cpus", + "once_cell", + "parking_lot", "pin-project-lite", + "signal-hook-registry", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index ed410d8..e8d3d0c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2018" [dependencies] steam_shortcuts_util={version="*", path="C:/src/Rust/steam_shortcuts_util"} -steamgriddb_api="*" +steamgriddb_api={version="*", path="C:/src/Rust/steamgriddb_api"} serde={version="*", features=["derive"]} -serde_json="*" \ No newline at end of file +serde_json="*" +tokio = { version = "1.11.0", features = ["full"] } +reqwest = { version = "*", features = ["default"] } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5de88e4..94c8a18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use std::{ + collections::HashMap, env::{self}, fmt, fs::File, @@ -11,6 +12,7 @@ use std::error::Error; use steam_shortcuts_util::{ parse_shortcuts, shortcut::ShortcutOwned, shortcuts_to_bytes, Shortcut, }; +use steamgriddb_api::{Client, query_parameters::{GridQueryParameters, HeroQueryParameters}, search::SearchResult}; pub struct ShortcutInfo { pub path: String, @@ -51,7 +53,8 @@ fn get_shortcuts_for_user(user: &SteamUsersInfo) -> ShortcutInfo { } } -fn main() -> Result<(), Box> { +#[tokio::main] +async fn main() -> Result<(), Box> { let auth_key = std::fs::read_to_string("auth_key.txt")?; let client = steamgriddb_api::Client::new(auth_key); @@ -63,30 +66,144 @@ fn main() -> Result<(), Box> { let userinfo_shortcuts = get_shortcuts_paths()?; println!("Found {} user(s)", userinfo_shortcuts.len()); - userinfo_shortcuts.iter().for_each(|user| { + for user in userinfo_shortcuts.iter() { let shortcut_info = get_shortcuts_for_user(user); - let user_shortcuts = shortcut_info.shortcuts; - let new_path = shortcut_info.path; - let new_user_shortcuts: Vec<&ShortcutOwned> = user_shortcuts + let new_user_shortcuts: Vec<&ShortcutOwned> = shortcut_info + .shortcuts .iter() .filter(|user_shortcut| !user_shortcut.tags.contains(&"EGS".to_owned())) .chain(egs_shortcuts.iter()) .collect(); - let shortcut_refs = new_user_shortcuts.iter().map(|f| f.borrow()).collect(); - let new_content = shortcuts_to_bytes(&shortcut_refs); + let shortcuts = new_user_shortcuts.iter().map(|f| f.borrow()).collect(); - let mut file = File::create(new_path).unwrap(); + let new_content = shortcuts_to_bytes(&shortcuts); + let mut file = File::create(shortcut_info.path).unwrap(); file.write(new_content.as_slice()).unwrap(); - // shortcut_refs.iter().map(|shortcut| { - // client.search(shortcut.app_name).await?.iter().next(); - // }); - }); + let known_images = get_users_images(user).unwrap(); + // let mut hash_map = HashMap::new(); + + let shortcuts_to_search_for = shortcuts.iter().filter(|s| { + let images = vec![ + format!("{}_hero.png", s.app_id), + format!("{}p.png", s.app_id), + format!("{}_logo.png", s.app_id), + ]; + // if we are missing any of the images we need to search for them + images.iter().any(|image| !known_images.contains(&image)) + }); + + let mut search_results = HashMap::new(); + for s in shortcuts_to_search_for { + println!("Searching for {}", s.app_name); + let search = search_for_shortcut(&client, s.app_name).await; + if let Some(search) = search { + search_results.insert(s.app_id, search); + } + } + + let types = vec![ImageType::Logo, ImageType::Hero, ImageType::Grid]; + for image_type in types { + let mut images_needed = shortcuts + .iter() + .filter(|s| search_results.contains_key(&s.app_id)) + .filter(|s| !known_images.contains(&image_type.file_name(s.app_id))); + let image_ids: Vec = images_needed + .clone() + .filter_map(|s| search_results.get(&s.app_id)) + .map(|search| search.id) + .collect(); + + + let animation_order= vec![ + steamgriddb_api::query_parameters::AnimtionType::Animated, + steamgriddb_api::query_parameters::AnimtionType::Static, + ]; + let hero_parameters = HeroQueryParameters { + types: Some(&animation_order), + ..HeroQueryParameters::default() + }; + let grid_parameters = GridQueryParameters { + types: Some(&animation_order), + ..GridQueryParameters::default() + }; + let query_type = match image_type { + ImageType::Hero => { + steamgriddb_api::query_parameters::QueryType::Hero(Some(hero_parameters)) + } + ImageType::Grid => steamgriddb_api::query_parameters::QueryType::Grid(Some(grid_parameters)), + ImageType::Logo => steamgriddb_api::query_parameters::QueryType::Logo(None), + }; + + match client + .get_images_for_ids(image_ids.as_slice(), &query_type) + .await + { + Ok(images) => { + for image in images { + if let Some(shortcut) = images_needed.next() { + if let Ok(image) = image { + let grid_folder = Path::new(user.steam_user_data_folder.as_str()) + .join("config/grid"); + let path = grid_folder.join(image_type.file_name(shortcut.app_id)); + println!( + "Downloading {} to {}", + image.url, + path.as_path().to_str().unwrap() + ); + let mut file = File::create(path).unwrap(); + let response = reqwest::get(image.url).await?; + let content = response.bytes().await?; + file.write(&content).unwrap(); + } + } + } + } + Err(err) => println!("Error getting images: {}", err), + } + } + } Ok(()) } +pub enum ImageType { + Hero, + Grid, + Logo, +} + +impl ImageType { + pub fn file_name(&self, app_id: u32) -> String { + match self { + ImageType::Hero => format!("{}_hero.png", app_id), + ImageType::Grid => format!("{}p.png", app_id), + ImageType::Logo => format!("{}_logo.png", app_id), + } + } +} + +async fn search_for_shortcut(client: &Client, name: &str) -> Option { + if let Ok(search) = client.search(name).await { + if let Some(search_ref) = search.iter().next() { + return Some(search_ref.to_owned()); + } + } + None +} + +fn get_users_images(user: &SteamUsersInfo) -> Result, Box> { + let grid_folder = Path::new(user.steam_user_data_folder.as_str()).join("config/grid"); + std::fs::create_dir_all(&grid_folder)?; + let user_folders = std::fs::read_dir(&grid_folder)?; + let file_names = user_folders + .filter_map(|image| image.ok()) + .map(|image| image.file_name().into_string().unwrap()) + .collect(); + Ok(file_names) +} + fn manifest_to_shortcut(manifest: &ManifestItem) -> ShortcutOwned { let exe = format!( "\"{}\\{}\"", @@ -107,6 +224,8 @@ fn manifest_to_shortcut(manifest: &ManifestItem) -> ShortcutOwned { ); let mut owned_shortcut = shortcut.to_owned(); owned_shortcut.tags.push("EGS".to_owned()); + owned_shortcut.tags.push("Ready TO Play".to_owned()); + owned_shortcut.tags.push("Installed".to_owned()); owned_shortcut }