diff --git a/configuration.md b/configuration.md index 17a698c..d46dad8 100644 --- a/configuration.md +++ b/configuration.md @@ -45,6 +45,8 @@ wine_c_drive="/home/username/Games/gog-galaxy/drive_c" #Only for Linux, Is manda [steam] location="C:\\Program Files (x86)\\Steam\\" #If this value is not defined, the tool will try to find it automatically. If it can't find it, it will fail and tell you. +optimize_for_big_picture=false #Set icons to wide images, that big picture mode will use. This will make the icons have a wrong ratio in desktop mode, but will improve the look in big picture mode +create_collections=false #Will try to create a steam collection for each platform [steamgrid_db] enabled = true #If false, the whole download of custom art will be skipped. diff --git a/src/defaultconfig.toml b/src/defaultconfig.toml index 809d34f..83d0e17 100644 --- a/src/defaultconfig.toml +++ b/src/defaultconfig.toml @@ -33,4 +33,5 @@ enabled = true enabled = true [steam] -create_collections = false \ No newline at end of file +create_collections = false +optimize_for_big_picture = false \ No newline at end of file diff --git a/src/egs/manifest_item.rs b/src/egs/manifest_item.rs index 2ce1e24..b8144fd 100644 --- a/src/egs/manifest_item.rs +++ b/src/egs/manifest_item.rs @@ -43,7 +43,7 @@ fn exe_shortcut(manifest: ManifestItem) -> ShortcutOwned { manifest.display_name.as_str(), exe, start_dir, - "", + exe, "", "", ) diff --git a/src/heroic/heroic_game.rs b/src/heroic/heroic_game.rs index 53c7f4a..32ccba0 100644 --- a/src/heroic/heroic_game.rs +++ b/src/heroic/heroic_game.rs @@ -45,7 +45,7 @@ impl From for ShortcutOwned { game.title.as_str(), &target, &install_path, - "", + &target, "", &game.launch_parameters.as_str(), ); diff --git a/src/steam/settings.rs b/src/steam/settings.rs index 6eedcc4..b3a3f0e 100644 --- a/src/steam/settings.rs +++ b/src/steam/settings.rs @@ -4,4 +4,5 @@ use serde::{Deserialize, Serialize}; pub struct SteamSettings { pub location: Option, pub create_collections: bool, + pub optimize_for_big_picture: bool, } diff --git a/src/steamgriddb/downloader.rs b/src/steamgriddb/downloader.rs index e81c18b..a74a8f0 100644 --- a/src/steamgriddb/downloader.rs +++ b/src/steamgriddb/downloader.rs @@ -44,6 +44,7 @@ pub async fn download_images_for_users<'b>( search, client, download_animated, + settings.steam.optimize_for_big_picture, ) .await; res.unwrap_or_default() @@ -78,6 +79,7 @@ pub async fn download_images_for_users<'b>( #[derive(Serialize, Deserialize, Debug, Clone)] pub struct PublicGameResponseMetadata { store_asset_mtime: Option, + clienticon: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -109,17 +111,25 @@ async fn search_fo_to_download( search: &CachedSearch<'_>, client: &Client, download_animated: bool, + download_big_picture: bool, ) -> Result, Box> { - 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!("{}.png", s.app_id), - format!("{}_logo.png", s.app_id), - format!("{}_bigpicture.png", s.app_id), + let types = { + let mut types = vec![ + ImageType::Logo, + ImageType::Hero, + ImageType::Grid, + ImageType::WideGrid, + ImageType::Icon, ]; + if download_big_picture { + types.push(ImageType::BigPicture); + } + types + }; + + let shortcuts_to_search_for = shortcuts.iter().filter(|s| { // if we are missing any of the images we need to search for them - images.iter().any(|image| !known_images.contains(image)) && !s.app_name.is_empty() + types.iter().map(|t| t.file_name(s.app_id)).any(|image| !known_images.contains(&image)) && !s.app_name.is_empty() }); let shortcuts_to_search_for: Vec<&ShortcutOwned> = shortcuts_to_search_for.collect(); if shortcuts_to_search_for.is_empty() { @@ -143,14 +153,9 @@ async fn search_fo_to_download( for (app_id, search) in search_results_a.into_iter().flatten() { search_results.insert(app_id, search); } - let types = vec![ - ImageType::Logo, - ImageType::Hero, - ImageType::Grid, - ImageType::WideGrid, - ImageType::BigPicture, - ]; + let mut to_download = vec![]; + let grid_folder = Path::new(user_data_folder).join("config").join("grid"); for image_type in types { let images_needed = shortcuts .iter() @@ -163,41 +168,59 @@ async fn search_fo_to_download( .collect(); let shortcuts: Vec<&ShortcutOwned> = images_needed.collect(); - let image_search_result = - get_images_for_ids(client, &image_ids, &image_type, download_animated).await; - match image_search_result { - Ok(images) => { - let grid_folder = Path::new(user_data_folder).join("config").join("grid"); - let images = images - .iter() - .enumerate() - .map(|(index, image)| (image, shortcuts[index], image_ids[index])); - let download_for_this_type = stream::iter(images) - .filter_map(|(image, shortcut, game_id)| { - let path = grid_folder.join(image_type.file_name(shortcut.app_id)); - async move { - let image_url = match image { - Ok(img) => Some(img.url.clone()), - Err(_) => get_steam_image_url(game_id, &image_type).await, - }; - if let Some(url) = image_url { - Some(ToDownload { - path, - url, - app_name: shortcut.app_name.clone(), - image_type: image_type.clone(), - }) - } else { - None - } - } - }) - .collect::>() - .await; + - to_download.extend(download_for_this_type); + if let ImageType::Icon = image_type { + let mut index = 0; + for image_id in image_ids{ + let shortcut = shortcuts[index]; + if let Some(url) = get_steam_icon_url(image_id).await{ + let path = grid_folder.join(image_type.file_name(shortcut.app_id)); + to_download.push(ToDownload { + path, + url, + app_name: shortcut.app_name.clone(), + image_type: image_type.clone(), + }); + } + index = index + 1; + } + } else { + let image_search_result = + get_images_for_ids(client, &image_ids, &image_type, download_animated).await; + match image_search_result { + Ok(images) => { + let images = images + .iter() + .enumerate() + .map(|(index, image)| (image, shortcuts[index], image_ids[index])); + let download_for_this_type = stream::iter(images) + .filter_map(|(image, shortcut, game_id)| { + let path = grid_folder.join(image_type.file_name(shortcut.app_id)); + async move { + let image_url = match image { + Ok(img) => Some(img.url.clone()), + Err(_) => get_steam_image_url(game_id, &image_type).await, + }; + if let Some(url) = image_url { + Some(ToDownload { + path, + url, + app_name: shortcut.app_name.clone(), + image_type: image_type.clone(), + }) + } else { + None + } + } + }) + .collect::>() + .await; + + to_download.extend(download_for_this_type); + } + Err(err) => println!("Error getting images: {}", err), } - Err(err) => println!("Error getting images: {}", err), } } Ok(to_download) @@ -244,15 +267,18 @@ async fn get_images_for_ids( nsfw: Some(&Nsfw::False), ..Default::default() }; + let query_type = match image_type { ImageType::Hero => Hero(Some(hero_parameters)), ImageType::BigPicture => Grid(Some(big_picture_parameters)), ImageType::Grid => Grid(Some(grid_parameters)), ImageType::WideGrid => Grid(Some(big_picture_parameters)), ImageType::Logo => Logo(Some(logo_parameters)), + _ => panic!("Unsupported image type"), }; let image_search_result = client.get_images_for_ids(image_ids, &query_type).await; + image_search_result } @@ -286,6 +312,43 @@ async fn get_steam_image_url(game_id: usize, image_type: &ImageType) -> Option Option { + let steamgriddb_page_url = format!("https://www.steamgriddb.com/api/public/game/{}/", game_id); + let response = reqwest::get(steamgriddb_page_url).await; + match response { + Ok(response) => { + let text_response = response.json::().await; + match text_response { + Ok(response) => { + let game_id = response + .data + .clone() + .map(|d| d.platforms.map(|p| p.steam.map(|s| s.id))); + let mtime = response.data.map(|d| { + d.platforms + .map(|p| p.steam.map(|s| s.metadata.map(|m| m.clienticon))) + }); + if let (Some(Some(Some(steam_app_id))), Some(Some(Some(Some(Some(mtime)))))) = + (game_id, mtime) + { + return Some(icon_url(&steam_app_id, &mtime)); + } + } + Err(_) => (), + } + } + Err(_) => (), + } + return None; +} + +fn icon_url(steam_app_id: &str, icon_id: &str) -> String { + format!( + "https://cdn.cloudflare.steamstatic.com/steamcommunity/public/images/apps/{}/{}.ico", + steam_app_id, icon_id + ) +} + async fn download_to_download(to_download: &ToDownload) -> Result<(), Box> { println!( "Downloading {:?} for {} to {:?}", diff --git a/src/steamgriddb/image_type.rs b/src/steamgriddb/image_type.rs index 4fbe043..aa241de 100644 --- a/src/steamgriddb/image_type.rs +++ b/src/steamgriddb/image_type.rs @@ -5,6 +5,7 @@ pub enum ImageType { WideGrid, Logo, BigPicture, + Icon } impl ImageType { @@ -15,6 +16,7 @@ impl ImageType { ImageType::WideGrid => format!("{}.png", app_id), ImageType::Logo => format!("{}_logo.png", app_id), ImageType::BigPicture => format!("{}_bigpicture.png", app_id), + ImageType::Icon => format!("{}.ico", app_id), } } @@ -25,7 +27,11 @@ impl ImageType { ImageType::Grid => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/library_600x900_2x.jpg?t={}",steam_app_id,mtime), ImageType::WideGrid => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg?t={}",steam_app_id,mtime), ImageType::Logo => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/logo.png?t={}",steam_app_id,mtime), - ImageType::BigPicture => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg?t={}",steam_app_id,mtime), - } + ImageType::BigPicture => format!("https://cdn.cloudflare.steamstatic.com/steam/apps/{}/header.jpg?t={}",steam_app_id,mtime), + // This should not happen + _ => "".to_string() + } } + + } diff --git a/src/sync/synchronization.rs b/src/sync/synchronization.rs index 04fa9cf..584e9eb 100644 --- a/src/sync/synchronization.rs +++ b/src/sync/synchronization.rs @@ -1,7 +1,7 @@ use steam_shortcuts_util::{shortcut::ShortcutOwned, shortcuts_to_bytes}; use crate::{ - egs::EpicPlatform, + egs::EpicPlatform, legendary::LegendaryPlatform, lutris::lutris_platform::LutrisPlatform, platform::Platform, @@ -10,7 +10,7 @@ use crate::{ get_shortcuts_for_user, get_shortcuts_paths, setup_proton_games, write_collections, Collection, ShortcutInfo, SteamUsersInfo, }, - steamgriddb::download_images_for_users, + steamgriddb::{download_images_for_users, ImageType}, uplay::Uplay, }; @@ -50,7 +50,11 @@ pub async fn run_sync(settings: &Settings) -> Result<(), Box> { shortcut_info.shortcuts.extend(all_shortcuts.clone()); - fix_shortcut_icons(user, &mut shortcut_info.shortcuts); + fix_shortcut_icons( + user, + &mut shortcut_info.shortcuts, + settings.steam.optimize_for_big_picture, + ); save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path)); @@ -86,10 +90,20 @@ fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) { .retain(|shortcut| !shortcut.dev_kit_game_id.starts_with(&boilr_tag)); } -fn fix_shortcut_icons(user: &SteamUsersInfo, shortcuts: &mut Vec) { +fn fix_shortcut_icons( + user: &SteamUsersInfo, + shortcuts: &mut Vec, + big_picture_mode: bool, +) { let image_folder = Path::new(&user.steam_user_data_folder) .join("config") .join("grid"); + let image_type = if big_picture_mode { + ImageType::BigPicture + } else { + ImageType::Icon + }; + for shortcut in shortcuts { #[cfg(not(target_family = "unix"))] let replace_icon = shortcut.icon.trim().eq(""); @@ -100,11 +114,10 @@ fn fix_shortcut_icons(user: &SteamUsersInfo, shortcuts: &mut Vec) &shortcut.exe, &shortcut.app_name, ); - let new_icon = image_folder - .join(format!("{}_bigpicture.png", app_id)) - .to_string_lossy() - .to_string(); - shortcut.icon = new_icon; + let new_icon_path = image_folder.join(image_type.file_name(app_id)); + if new_icon_path.exists() { + shortcut.icon = new_icon_path.to_string_lossy().to_string(); + } } } } @@ -144,7 +157,6 @@ fn get_platform_shortcuts(settings: &Settings) -> Vec<(String, Vec