mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
+4
-4
@@ -64,10 +64,10 @@ mod tests {
|
||||
// Parameters for set environment 'XDG_CONFIG_HOME'
|
||||
std::env::set_var(
|
||||
"XDG_CONFIG_HOME",
|
||||
std::env::var("HOME").unwrap() + "/.config/boilr",
|
||||
std::env::var("HOME").unwrap_or_default() + "/.config/boilr",
|
||||
);
|
||||
|
||||
let xdg_config_home = std::env::var("XDG_CONFIG_HOME").unwrap();
|
||||
let xdg_config_home = std::env::var("XDG_CONFIG_HOME").unwrap_or_default();
|
||||
let config_path = get_config_folder();
|
||||
let test_path = PathBuf::from(xdg_config_home);
|
||||
|
||||
@@ -79,11 +79,11 @@ mod tests {
|
||||
fn check_return_config_path() {
|
||||
std::env::set_var(
|
||||
"XDG_CONFIG_HOME",
|
||||
std::env::var("HOME").unwrap() + "/.config/boilr",
|
||||
std::env::var("HOME").unwrap_or_default() + "/.config/boilr",
|
||||
);
|
||||
|
||||
let config_path = get_config_folder();
|
||||
let current_path = std::env::var("HOME").unwrap() + "/.config/boilr";
|
||||
let current_path = std::env::var("HOME").unwrap_or_default() + "/.config/boilr";
|
||||
|
||||
assert_eq!(config_path, PathBuf::from(current_path));
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,3 +1,7 @@
|
||||
#![deny(clippy::unwrap_in_result)]
|
||||
#![deny(clippy::get_unwrap)]
|
||||
#![deny(clippy::unwrap_used)]
|
||||
|
||||
mod config;
|
||||
mod migration;
|
||||
mod platforms;
|
||||
@@ -16,9 +20,9 @@ fn main() -> Result<()> {
|
||||
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
if args.contains(&"--no-ui".to_string()) {
|
||||
ui::run_sync();
|
||||
ui::run_sync()?;
|
||||
} else {
|
||||
ui::run_ui(args);
|
||||
ui::run_ui(args)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+3
-1
@@ -38,7 +38,9 @@ pub fn migrate_config() {
|
||||
if let Ok(mut settings) = crate::settings::Settings::new() {
|
||||
settings.config_version = Some(1);
|
||||
let platforms = get_platforms();
|
||||
save_settings(&settings, &platforms);
|
||||
if let Err(err) = save_settings(&settings, &platforms){
|
||||
eprintln!("Failed to load settings {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +185,9 @@ impl ManifestItem {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
//Okay to unwrap in tests
|
||||
#![allow(clippy::unwrap_in_result)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -42,7 +42,7 @@ impl From<GogShortcut> for ShortcutOwned {
|
||||
let icon_file = format!("goggame-{}.ico", gogs.game_id);
|
||||
let icon_path = Path::new(&gogs.game_folder).join(icon_file);
|
||||
let icon = if icon_path.exists() {
|
||||
icon_path.to_str().unwrap().to_string()
|
||||
icon_path.to_string_lossy().to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
@@ -62,6 +62,9 @@ fn parse_path(i: &[u8]) -> nom::IResult<&[u8], DbPaths> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//Okay to unwrap in tests
|
||||
#![allow(clippy::unwrap_in_result)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct ItchGame {
|
||||
impl From<ItchGame> for ShortcutOwned {
|
||||
fn from(game: ItchGame) -> Self {
|
||||
let exe = Path::new(&game.install_path).join(&game.executable);
|
||||
let exe = exe.to_str().unwrap().to_string();
|
||||
let exe = exe.to_string_lossy().to_string();
|
||||
let shortcut = Shortcut::new(
|
||||
"0",
|
||||
game.title.as_str(),
|
||||
|
||||
@@ -29,7 +29,7 @@ impl ItchPlatform {
|
||||
));
|
||||
}
|
||||
|
||||
let shortcut_bytes = std::fs::read(&itch_db_location).unwrap();
|
||||
let shortcut_bytes = std::fs::read(&itch_db_location)?;
|
||||
|
||||
let paths = match parse_butler_db(&shortcut_bytes) {
|
||||
Ok((_, shortcuts)) => Ok(shortcuts),
|
||||
@@ -59,17 +59,19 @@ fn dbpath_to_game(paths: &DbPaths) -> Option<ItchGame> {
|
||||
.iter()
|
||||
.filter(|p| Path::new(&paths.base_path).join(p).is_executable())
|
||||
.find_map(|executable| {
|
||||
let gz_bytes = std::fs::read(&recipt).unwrap();
|
||||
let mut d = GzDecoder::new(gz_bytes.as_slice());
|
||||
let mut s = String::new();
|
||||
d.read_to_string(&mut s).unwrap();
|
||||
|
||||
let receipt_op: Option<Receipt> = serde_json::from_str(&s).ok();
|
||||
receipt_op.map(|re| ItchGame {
|
||||
install_path: paths.base_path.to_owned(),
|
||||
executable: executable.to_owned(),
|
||||
title: re.game.title,
|
||||
})
|
||||
if let Ok(gz_bytes) = std::fs::read(&recipt) {
|
||||
let mut d = GzDecoder::new(gz_bytes.as_slice());
|
||||
let mut s = String::new();
|
||||
if d.read_to_string(&mut s).is_ok() {
|
||||
let receipt_op: Option<Receipt> = serde_json::from_str(&s).ok();
|
||||
return receipt_op.map(|re| ItchGame {
|
||||
install_path: paths.base_path.to_owned(),
|
||||
executable: executable.to_owned(),
|
||||
title: re.game.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -32,11 +32,7 @@ impl NeedsPorton<OriginPlatform> for OriginGame {
|
||||
|
||||
impl OriginPlatform {
|
||||
fn get_shortcuts(&self) -> eyre::Result<Vec<OriginGame>> {
|
||||
let origin_folders = get_default_locations();
|
||||
if origin_folders.is_none() {
|
||||
return Err(eyre::format_err!("Default path not found"));
|
||||
}
|
||||
let origin_folders = origin_folders.unwrap();
|
||||
let origin_folders = get_default_locations().ok_or(eyre::format_err!("Default path not found"))?;
|
||||
let origin_folder = origin_folders.local_content_path;
|
||||
let origin_exe = origin_folders.exe_path;
|
||||
let game_folders = origin_folder.join("LocalContent").read_dir()?;
|
||||
|
||||
+4
-3
@@ -69,8 +69,8 @@ pub fn load_setting_sections() -> eyre::Result<HashMap<String, String>> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn save_settings(settings: &Settings, platforms: &Platforms) {
|
||||
let mut toml = toml::to_string(&settings).unwrap();
|
||||
pub fn save_settings(settings: &Settings, platforms: &Platforms) -> eyre::Result<()>{
|
||||
let mut toml = toml::to_string(&settings)?;
|
||||
|
||||
for platform in platforms {
|
||||
let section_name = format!("[{}]", platform.code_name());
|
||||
@@ -82,7 +82,8 @@ pub fn save_settings(settings: &Settings, platforms: &Platforms) {
|
||||
}
|
||||
|
||||
let config_path = crate::config::get_config_file();
|
||||
std::fs::write(config_path, toml).unwrap();
|
||||
std::fs::write(config_path, toml)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_sections(
|
||||
|
||||
+17
-16
@@ -158,7 +158,7 @@ pub fn write_collections<S: AsRef<str>>(
|
||||
&vdf_collections,
|
||||
);
|
||||
if let Some(new_string) = new_string {
|
||||
std::fs::write(path, new_string).unwrap();
|
||||
std::fs::write(path, new_string)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,22 +242,19 @@ fn get_categories<S: AsRef<str>>(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn open_db() -> Result<DB, Box<dyn Error>> {
|
||||
let location = get_level_db_location();
|
||||
fn open_db() -> eyre::Result<DB> {
|
||||
use eyre::eyre;
|
||||
let location = get_level_db_location().ok_or(eyre!("Collections db not found"))?;
|
||||
let options = Options::default();
|
||||
let open_res = DB::open(location.unwrap(), options);
|
||||
if let Err(e) = &open_res {
|
||||
match &e.code {
|
||||
rusty_leveldb::StatusCode::LockError => {
|
||||
println!("Could not lock the steam level database, make sure steam is turned off when running synchronizations");
|
||||
}
|
||||
rusty_leveldb::StatusCode::NotFound => {
|
||||
println!("Could not find the steam level database, try to open and close steam once and synchronize again");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
Ok(open_res?)
|
||||
let open_res = DB::open(location, options);
|
||||
open_res.map_err(|e|{
|
||||
use rusty_leveldb::StatusCode::*;
|
||||
match e.code{
|
||||
LockError => eyre!("Could not lock the steam level database, make sure steam is turned off when running synchronizations"),
|
||||
NotFound => eyre!("Could not find the steam level database, try to open and close steam once and synchronize again"),
|
||||
_ => eyre!("Failed opening collections file: {}",e.err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn get_namespace_keys<S: AsRef<str>>(steamid: S, db: &mut DB) -> HashSet<String> {
|
||||
@@ -394,6 +391,10 @@ pub struct VdfCollection {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//Allow unwraps in test
|
||||
#![allow(clippy::unwrap_in_result)]
|
||||
#![allow(clippy::get_unwrap)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -66,19 +66,25 @@ fn parse_manifest_string<S: AsRef<str>>(string: S) -> Option<SteamGameInfo> {
|
||||
let app_id_line = lines.find(|l| l.contains("\"appid\""));
|
||||
let name_line = lines.find(|l| l.contains("\"name\""));
|
||||
match (app_id_line, name_line) {
|
||||
(Some(app_id_line), Some(name_line)) => Some(SteamGameInfo {
|
||||
name: name_line[10..name_line.len() - 1].to_string(),
|
||||
appid: app_id_line[11..app_id_line.len() - 1]
|
||||
.to_string()
|
||||
.parse()
|
||||
.unwrap(),
|
||||
}),
|
||||
(Some(app_id_line), Some(name_line)) => {
|
||||
let appid = app_id_line[11..app_id_line.len() - 1].to_string().parse();
|
||||
match appid {
|
||||
Ok(appid) => Some(SteamGameInfo {
|
||||
name: name_line[10..name_line.len() - 1].to_string(),
|
||||
appid,
|
||||
}),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//Okay to unwrap in tests
|
||||
#![allow(clippy::unwrap_in_result)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -2,16 +2,17 @@ use std::path::Path;
|
||||
|
||||
use nom::FindSubstring;
|
||||
|
||||
pub fn setup_proton_games<B: AsRef<str>>(games: &[B]) {
|
||||
pub fn setup_proton_games<B: AsRef<str>>(games: &[B]) -> eyre::Result<()>{
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
let config_file = Path::new(&home).join(".local/share/Steam/config/config.vdf");
|
||||
if config_file.exists() {
|
||||
if let Ok(config_content) = std::fs::read_to_string(&config_file) {
|
||||
let new_string = enable_proton_games(config_content, games);
|
||||
std::fs::write(config_file, new_string).unwrap();
|
||||
std::fs::write(config_file, new_string)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn enable_proton_games<S: AsRef<str>, B: AsRef<str>>(vdf_content: S, games: &[B]) -> String {
|
||||
@@ -96,6 +97,9 @@ fn find_indexes<S: AsRef<str>>(vdf_content: S) -> Option<SectionInfo> {
|
||||
#[cfg(target_family = "unix")]
|
||||
mod tests {
|
||||
|
||||
//Okay to unwrap in tests
|
||||
#![allow(clippy::unwrap_in_result)]
|
||||
#![allow(clippy::unwrap_used)]
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
||||
+20
-20
@@ -8,14 +8,14 @@ use steam_shortcuts_util::{parse_shortcuts, shortcut::ShortcutOwned};
|
||||
|
||||
use super::SteamSettings;
|
||||
|
||||
pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> ShortcutInfo {
|
||||
pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> eyre::Result<ShortcutInfo> {
|
||||
let mut shortcuts = vec![];
|
||||
|
||||
let new_path = match &user.shortcut_path {
|
||||
Some(shortcut_path) => {
|
||||
let content = std::fs::read(shortcut_path).unwrap();
|
||||
let content = std::fs::read(shortcut_path)?;
|
||||
shortcuts = parse_shortcuts(content.as_slice())
|
||||
.unwrap()
|
||||
.map_err(|e| eyre::format_err!("Could not parse shortcuts: {:?}", e))?
|
||||
.iter()
|
||||
.map(|s| s.to_owned())
|
||||
.collect();
|
||||
@@ -27,15 +27,15 @@ pub fn get_shortcuts_for_user(user: &SteamUsersInfo) -> ShortcutInfo {
|
||||
user.steam_user_data_folder
|
||||
);
|
||||
let path = Path::new(&user.steam_user_data_folder).join("config");
|
||||
std::fs::create_dir_all(path.clone()).unwrap();
|
||||
std::fs::create_dir_all(path.clone())?;
|
||||
path.join("shortcuts.vdf")
|
||||
}
|
||||
};
|
||||
|
||||
ShortcutInfo {
|
||||
Ok(ShortcutInfo {
|
||||
shortcuts,
|
||||
path: new_path,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ShortcutInfo {
|
||||
@@ -51,22 +51,22 @@ pub struct SteamUsersInfo {
|
||||
}
|
||||
|
||||
/// Get the paths to the steam users shortcuts (one for each user)
|
||||
pub fn get_shortcuts_paths(
|
||||
settings: &SteamSettings,
|
||||
) -> Result<Vec<SteamUsersInfo>, Box<dyn Error + Sync + Send>> {
|
||||
pub fn get_shortcuts_paths(settings: &SteamSettings) -> eyre::Result<Vec<SteamUsersInfo>> {
|
||||
let steam_path_str = get_steam_path(settings)?;
|
||||
let steam_path = Path::new(&steam_path_str);
|
||||
if !steam_path.exists() {
|
||||
return Result::Err(Box::new(SteamFolderNotFound {
|
||||
location_tried: format!("{:?}", steam_path),
|
||||
}));
|
||||
return Err(eyre::format_err!(
|
||||
"Steam folder not found at: {:?}",
|
||||
steam_path
|
||||
));
|
||||
}
|
||||
|
||||
let user_data_path = steam_path.join("userdata");
|
||||
if !user_data_path.exists() {
|
||||
return Result::Err(Box::new(SteamFolderNotFound {
|
||||
location_tried: format!("{:?}", user_data_path),
|
||||
}));
|
||||
return Err(eyre::format_err!(
|
||||
"Steam user data folder not found at: {:?}",
|
||||
user_data_path
|
||||
));
|
||||
}
|
||||
|
||||
if !user_data_path.exists() {}
|
||||
@@ -89,7 +89,7 @@ pub fn get_shortcuts_paths(
|
||||
if shortcuts_path.exists() {
|
||||
return SteamUsersInfo {
|
||||
steam_user_data_folder: folder_string,
|
||||
shortcut_path: Some(shortcuts_path.to_str().unwrap().to_string()),
|
||||
shortcut_path: Some(shortcuts_path.to_string_lossy().to_string()),
|
||||
user_id,
|
||||
};
|
||||
} else {
|
||||
@@ -104,7 +104,7 @@ pub fn get_shortcuts_paths(
|
||||
Ok(users_info)
|
||||
}
|
||||
|
||||
pub fn get_steam_path(settings: &SteamSettings) -> Result<String, Box<dyn Error + Sync + Send>> {
|
||||
pub fn get_steam_path(settings: &SteamSettings) -> eyre::Result<String> {
|
||||
let user_location = settings.location.clone();
|
||||
let steam_path_str = match user_location {
|
||||
Some(location) => location,
|
||||
@@ -113,7 +113,7 @@ pub fn get_steam_path(settings: &SteamSettings) -> Result<String, Box<dyn Error
|
||||
Ok(steam_path_str)
|
||||
}
|
||||
|
||||
pub fn get_default_location() -> Result<String, Box<dyn Error + Sync + Send>> {
|
||||
pub fn get_default_location() -> eyre::Result<String> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let path_string = {
|
||||
let key = "PROGRAMFILES(X86)";
|
||||
@@ -198,8 +198,8 @@ impl Error for SteamUsersDataEmpty {
|
||||
self.location_tried.as_str()
|
||||
}
|
||||
}
|
||||
pub fn get_users_images(user: &SteamUsersInfo) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
let grid_folder = Path::new(user.steam_user_data_folder.as_str()).join("config/grid");
|
||||
pub fn get_users_images(data_folder: &str) -> Result<Vec<String>, Box<dyn Error>> {
|
||||
let grid_folder = Path::new(data_folder).join("config/grid");
|
||||
if !grid_folder.exists() {
|
||||
std::fs::create_dir_all(&grid_folder)?;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ impl<'a> CachedSearch<'a> {
|
||||
}
|
||||
|
||||
pub fn save(&self) {
|
||||
save_search_map(&self.search_map);
|
||||
if let Err(err) = save_search_map(&self.search_map){
|
||||
eprintln!("Failed saving searchmap : {:?}",err);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_cache<S>(&mut self, app_id: u32, name: S, new_grid_id: usize)
|
||||
@@ -58,16 +60,21 @@ impl<'a> CachedSearch<'a> {
|
||||
fn get_search_map() -> SearchMap {
|
||||
let path = get_cache_file();
|
||||
if path.exists() {
|
||||
let string = std::fs::read_to_string(path).unwrap();
|
||||
serde_json::from_str::<SearchMap>(&string).expect("Failed to parse cache.json")
|
||||
std::fs::read_to_string(path)
|
||||
.ok()
|
||||
.and_then(|string| {
|
||||
serde_json::from_str::<SearchMap>(&string).ok()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
SearchMap::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn save_search_map(search_map: &SearchMap) {
|
||||
let string = serde_json::to_string(search_map).unwrap();
|
||||
fn save_search_map(search_map: &SearchMap) -> eyre::Result<()> {
|
||||
let string = serde_json::to_string(search_map)?;
|
||||
let path = get_cache_file();
|
||||
let mut file = File::create(path).unwrap();
|
||||
file.write_all(string.as_bytes()).unwrap();
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(string.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,10 +23,31 @@ use crate::sync::SyncProgress;
|
||||
|
||||
const CONCURRENT_REQUESTS: usize = 10;
|
||||
|
||||
impl SearchSettings for Settings {
|
||||
fn download_animated(&self) -> bool {
|
||||
self.steamgrid_db.prefer_animated
|
||||
}
|
||||
|
||||
fn download_big_picture(&self) -> bool {
|
||||
self.steam.optimize_for_big_picture
|
||||
}
|
||||
|
||||
fn allow_nsfw(&self) -> bool {
|
||||
self.steamgrid_db.allow_nsfw
|
||||
}
|
||||
|
||||
fn only_download_boilr_images(&self) -> bool {
|
||||
self.steamgrid_db.only_download_boilr_images
|
||||
}
|
||||
|
||||
fn is_image_banned(&self, image_type: &ImageType, app_id: u32) -> bool {
|
||||
self.steamgrid_db.is_image_banned(image_type, app_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn download_images_for_users<'b>(
|
||||
settings: &Settings,
|
||||
users: &[SteamUsersInfo],
|
||||
download_animated: bool,
|
||||
sender: &mut Option<Sender<SyncProgress>>,
|
||||
) {
|
||||
let auth_key = &settings.steamgrid_db.auth_key;
|
||||
@@ -40,24 +61,29 @@ pub async fn download_images_for_users<'b>(
|
||||
if let Some(sender) = sender {
|
||||
let _ = sender.send(SyncProgress::FindingImages);
|
||||
}
|
||||
let to_downloads = stream::iter(users)
|
||||
.map(|user| {
|
||||
let shortcut_info = get_shortcuts_for_user(user);
|
||||
async move {
|
||||
let known_images = get_users_images(user).unwrap_or_default();
|
||||
let res = search_for_images_to_download(
|
||||
known_images,
|
||||
user.steam_user_data_folder.as_str(),
|
||||
&shortcut_info.shortcuts,
|
||||
search,
|
||||
client,
|
||||
download_animated,
|
||||
settings.steam.optimize_for_big_picture,
|
||||
settings,
|
||||
)
|
||||
.await;
|
||||
res.unwrap_or_default()
|
||||
}
|
||||
|
||||
let users_info = users.iter().filter_map(|user| {
|
||||
let shortcut_info = get_shortcuts_for_user(user);
|
||||
shortcut_info
|
||||
.map(|shortcut_info| {
|
||||
let data_folder = &user.steam_user_data_folder;
|
||||
(shortcut_info, data_folder)
|
||||
})
|
||||
.ok()
|
||||
});
|
||||
let to_downloads = stream::iter(users_info)
|
||||
.map(|(shortcut_info, data_folder)| async move {
|
||||
let known_images = get_users_images(data_folder).unwrap_or_default();
|
||||
let res = search_for_images_to_download(
|
||||
known_images,
|
||||
data_folder.as_str(),
|
||||
&shortcut_info.shortcuts,
|
||||
search,
|
||||
client,
|
||||
settings,
|
||||
)
|
||||
.await;
|
||||
res.unwrap_or_default()
|
||||
})
|
||||
.buffer_unordered(CONCURRENT_REQUESTS)
|
||||
.collect::<Vec<Vec<ToDownload>>>()
|
||||
@@ -128,15 +154,21 @@ pub struct PublicGameResponse {
|
||||
data: Option<PublicGameResponseData>,
|
||||
}
|
||||
|
||||
async fn search_for_images_to_download(
|
||||
pub trait SearchSettings {
|
||||
fn download_animated(&self) -> bool;
|
||||
fn download_big_picture(&self) -> bool;
|
||||
fn allow_nsfw(&self) -> bool;
|
||||
fn only_download_boilr_images(&self) -> bool;
|
||||
fn is_image_banned(&self, image_type: &ImageType, app_id: u32) -> bool;
|
||||
}
|
||||
|
||||
async fn search_for_images_to_download<T: SearchSettings>(
|
||||
known_images: Vec<String>,
|
||||
user_data_folder: &str,
|
||||
shortcuts: &[ShortcutOwned],
|
||||
search: &CachedSearch<'_>,
|
||||
client: &Client,
|
||||
download_animated: bool,
|
||||
download_big_picture: bool,
|
||||
settings: &Settings,
|
||||
search_settins: &T,
|
||||
) -> Result<Vec<ToDownload>, Box<dyn Error>> {
|
||||
let types = {
|
||||
let mut types = vec![
|
||||
@@ -146,7 +178,7 @@ async fn search_for_images_to_download(
|
||||
ImageType::WideGrid,
|
||||
ImageType::Icon,
|
||||
];
|
||||
if download_big_picture {
|
||||
if search_settins.download_big_picture() {
|
||||
types.push(ImageType::BigPicture);
|
||||
}
|
||||
types
|
||||
@@ -154,7 +186,7 @@ async fn search_for_images_to_download(
|
||||
|
||||
let shortcuts_to_search_for = shortcuts
|
||||
.iter()
|
||||
.filter(|s| !settings.steamgrid_db.only_download_boilr_images || s.is_boilr_shortcut())
|
||||
.filter(|s| !search_settins.only_download_boilr_images() || s.is_boilr_shortcut())
|
||||
.filter(|s| {
|
||||
// if we are missing any of the images we need to search for them
|
||||
types
|
||||
@@ -171,13 +203,10 @@ async fn search_for_images_to_download(
|
||||
let search_results_a = stream::iter(shortcuts_to_search_for)
|
||||
.map(|s| async move {
|
||||
let search_result = search.search(s.app_id, &s.app_name).await;
|
||||
if search_result.is_err() {
|
||||
return None;
|
||||
match search_result {
|
||||
Ok(Some(search_result)) => Some((s.app_id, search_result)),
|
||||
_ => None,
|
||||
}
|
||||
let search_result = search_result.unwrap();
|
||||
search_result?;
|
||||
let search_result = search_result.unwrap();
|
||||
Some((s.app_id, search_result))
|
||||
})
|
||||
.buffer_unordered(CONCURRENT_REQUESTS)
|
||||
.collect::<Vec<Option<(u32, usize)>>>()
|
||||
@@ -192,7 +221,7 @@ async fn search_for_images_to_download(
|
||||
let images_needed = shortcuts
|
||||
.iter()
|
||||
.filter(|s| search_results.contains_key(&s.app_id))
|
||||
.filter(|s| !settings.steamgrid_db.is_image_banned(&image_type, s.app_id))
|
||||
.filter(|s| !search_settins.is_image_banned(&image_type, s.app_id))
|
||||
.filter(|s| !known_images.contains(&image_type.file_name_no_extension(s.app_id)));
|
||||
let image_ids: Vec<usize> = images_needed
|
||||
.clone()
|
||||
@@ -203,8 +232,14 @@ async fn search_for_images_to_download(
|
||||
let shortcuts: Vec<&ShortcutOwned> = images_needed.collect();
|
||||
|
||||
for image_ids in image_ids.chunks(99) {
|
||||
let image_search_result =
|
||||
get_images_for_ids(client, image_ids, &image_type, download_animated, settings.steamgrid_db.allow_nsfw).await;
|
||||
let image_search_result = get_images_for_ids(
|
||||
client,
|
||||
image_ids,
|
||||
&image_type,
|
||||
search_settins.download_animated(),
|
||||
search_settins.allow_nsfw(),
|
||||
)
|
||||
.await;
|
||||
match image_search_result {
|
||||
Ok(images) => {
|
||||
let images = images
|
||||
@@ -237,7 +272,7 @@ async fn search_for_images_to_download(
|
||||
|
||||
to_download.extend(download_for_this_type);
|
||||
}
|
||||
Err(err) => println!("Error getting images: {}", err),
|
||||
Err(err) => eprintln!("Error getting images: {}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,17 +423,17 @@ fn icon_url(steam_app_id: &str, icon_id: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn download_to_download(to_download: &ToDownload) -> Result<(), Box<dyn Error>> {
|
||||
pub async fn download_to_download(to_download: &ToDownload) -> eyre::Result<()> {
|
||||
println!(
|
||||
"Downloading {:?} for {} to {:?}",
|
||||
to_download.image_type, to_download.app_name, to_download.path
|
||||
);
|
||||
let path = &to_download.path;
|
||||
let url = &to_download.url;
|
||||
let mut file = File::create(path).unwrap();
|
||||
let mut file = File::create(path)?;
|
||||
let response = reqwest::get(url).await?;
|
||||
let content = response.bytes().await?;
|
||||
file.write_all(&content).unwrap();
|
||||
file.write_all(&content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+36
-27
@@ -34,15 +34,16 @@ pub fn disconnect_shortcut(settings: &Settings, app_id: u32) -> Result<(), Strin
|
||||
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
|
||||
|
||||
for user in userinfo_shortcuts.iter_mut() {
|
||||
let mut shortcut_info = get_shortcuts_for_user(user);
|
||||
|
||||
for shortcut in shortcut_info.shortcuts.iter_mut() {
|
||||
if shortcut.app_id == app_id {
|
||||
shortcut.dev_kit_game_id = "".to_string();
|
||||
shortcut.tags.retain(|s| s != BOILR_TAG);
|
||||
let shortcut_info = get_shortcuts_for_user(user);
|
||||
if let Ok(mut shortcut_info) = shortcut_info {
|
||||
for shortcut in shortcut_info.shortcuts.iter_mut() {
|
||||
if shortcut.app_id == app_id {
|
||||
shortcut.dev_kit_game_id = "".to_string();
|
||||
shortcut.tags.retain(|s| s != BOILR_TAG);
|
||||
}
|
||||
}
|
||||
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
||||
}
|
||||
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -53,9 +54,8 @@ pub fn sync_shortcuts(
|
||||
platform_shortcuts: &[(String, Vec<ShortcutOwned>)],
|
||||
sender: &mut Option<Sender<SyncProgress>>,
|
||||
renames: &HashMap<u32, String>,
|
||||
) -> Result<Vec<SteamUsersInfo>, String> {
|
||||
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
|
||||
.map_err(|e| format!("Getting shortcut paths failed: {e}"))?;
|
||||
) -> eyre::Result<Vec<SteamUsersInfo>> {
|
||||
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)?;
|
||||
let mut all_shortcuts: Vec<ShortcutOwned> = platform_shortcuts
|
||||
.iter()
|
||||
.flat_map(|s| s.1.clone())
|
||||
@@ -86,10 +86,14 @@ pub fn sync_shortcuts(
|
||||
println!("Appid: {} name: {}", shortcut.app_id, shortcut.app_name);
|
||||
}
|
||||
println!("Found {} user(s)", userinfo_shortcuts.len());
|
||||
for user in userinfo_shortcuts.iter_mut() {
|
||||
let ok_shorcuts = userinfo_shortcuts.iter_mut().filter_map(|user|{
|
||||
let shortcut_info = get_shortcuts_for_user(user).ok();
|
||||
shortcut_info.map(|shortcut_info| {
|
||||
(user,shortcut_info)
|
||||
})
|
||||
});
|
||||
for (user,mut shortcut_info) in ok_shorcuts {
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
let mut shortcut_info = get_shortcuts_for_user(user);
|
||||
println!(
|
||||
"Found {} shortcuts for user: {}",
|
||||
shortcut_info.shortcuts.len(),
|
||||
@@ -122,11 +126,12 @@ pub async fn download_images(
|
||||
sender: &mut Option<Sender<SyncProgress>>,
|
||||
) {
|
||||
if settings.steamgrid_db.enabled {
|
||||
if settings.steamgrid_db.prefer_animated {
|
||||
println!("downloading animated images");
|
||||
download_images_for_users(settings, userinfo_shortcuts, true, sender).await;
|
||||
download_images_for_users(settings, userinfo_shortcuts, sender).await;
|
||||
if settings.steamgrid_db.prefer_animated{
|
||||
let mut set = settings.clone();
|
||||
set.steamgrid_db.prefer_animated = false;
|
||||
download_images_for_users(&set, userinfo_shortcuts, sender).await;
|
||||
}
|
||||
download_images_for_users(settings, userinfo_shortcuts, false, sender).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,21 +162,25 @@ fn remove_old_shortcuts(shortcut_info: &mut ShortcutInfo) {
|
||||
.retain(|shortcut| !shortcut.is_boilr_shortcut());
|
||||
}
|
||||
|
||||
pub fn fix_all_shortcut_icons (
|
||||
settings: &Settings,
|
||||
) -> eyre::Result<()>{
|
||||
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam).map_err(|e|eyre::format_err!("Could not find steam shortcuts; {e}"))?;
|
||||
pub fn fix_all_shortcut_icons(settings: &Settings) -> eyre::Result<()> {
|
||||
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)
|
||||
.map_err(|e| eyre::format_err!("Could not find steam shortcuts; {e}"))?;
|
||||
for user in userinfo_shortcuts.iter_mut() {
|
||||
let mut shortcut_info = get_shortcuts_for_user(user);
|
||||
let changes = fix_shortcut_icons(user,&mut shortcut_info.shortcuts,settings.steam.optimize_for_big_picture);
|
||||
if changes{
|
||||
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
||||
let shortcut_info = get_shortcuts_for_user(user);
|
||||
if let Ok(mut shortcut_info) = shortcut_info {
|
||||
let changes = fix_shortcut_icons(
|
||||
user,
|
||||
&mut shortcut_info.shortcuts,
|
||||
settings.steam.optimize_for_big_picture,
|
||||
);
|
||||
if changes {
|
||||
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn fix_shortcut_icons(
|
||||
user: &SteamUsersInfo,
|
||||
shortcuts: &mut Vec<ShortcutOwned>,
|
||||
@@ -194,7 +203,7 @@ fn fix_shortcut_icons(
|
||||
let path = image_folder.join(image_type.file_name(app_id, ext));
|
||||
if !icon_exsists && path.exists() {
|
||||
shortcut.icon = path.to_string_lossy().to_string();
|
||||
has_changes= true;
|
||||
has_changes = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,147 +1,186 @@
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use egui::{Button, ImageButton};
|
||||
use futures::executor::block_on;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::steamgriddb::{ImageType, ToDownload};
|
||||
use crate::steamgriddb::{ToDownload, ImageType};
|
||||
use crate::ui::images::{clamp_to_width, ImageHandles, TextureDownloadState};
|
||||
use crate::ui::ui_images::load_image_from_path;
|
||||
|
||||
|
||||
pub fn render_image_from_path(
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
path: &Path,
|
||||
pub struct GameButton {
|
||||
path: PathBuf,
|
||||
max_width: f32,
|
||||
text: &str,
|
||||
) -> bool {
|
||||
render_possible_image(ui, image_handles, path, max_width, text, &ImageType::Grid, None, None)
|
||||
text: String,
|
||||
image_type: ImageType,
|
||||
}
|
||||
|
||||
pub fn render_image_from_path_image_type(
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
path: &Path,
|
||||
max_width: f32,
|
||||
text: &str,
|
||||
image_type: &ImageType,
|
||||
) -> bool {
|
||||
render_possible_image(ui, image_handles, path, max_width, text, image_type, None, None)
|
||||
}
|
||||
|
||||
|
||||
pub fn render_image_from_path_or_url(
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
path: &Path,
|
||||
max_width: f32,
|
||||
text: &str,
|
||||
image_type: &ImageType,
|
||||
rt: &Runtime,
|
||||
url: &str,
|
||||
) -> bool {
|
||||
render_possible_image(
|
||||
ui,
|
||||
image_handles,
|
||||
path,
|
||||
max_width,
|
||||
text,
|
||||
image_type,
|
||||
Some(rt),
|
||||
Some(url),
|
||||
)
|
||||
}
|
||||
|
||||
fn render_possible_image(
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
path: &Path,
|
||||
max_width: f32,
|
||||
text: &str,
|
||||
image_type: &ImageType,
|
||||
rt: Option<&Runtime>,
|
||||
url: Option<&str>,
|
||||
) -> bool {
|
||||
let image_key = path.to_string_lossy().to_string();
|
||||
|
||||
match image_handles.get_mut(&image_key) {
|
||||
Some(mut state) => {
|
||||
match state.value() {
|
||||
TextureDownloadState::Downloading => {
|
||||
ui.ctx().request_repaint();
|
||||
//nothing to do,just wait
|
||||
ui.spinner();
|
||||
}
|
||||
TextureDownloadState::Downloaded => {
|
||||
//Need to load
|
||||
let image_data = load_image_from_path(path);
|
||||
match image_data {
|
||||
Ok(image_data) => {
|
||||
let handle = ui.ctx().load_texture(
|
||||
&image_key,
|
||||
image_data,
|
||||
egui::TextureOptions::LINEAR,
|
||||
);
|
||||
*state.value_mut() = TextureDownloadState::Loaded(handle);
|
||||
ui.spinner();
|
||||
}
|
||||
Err(_) => *state.value_mut() = TextureDownloadState::Failed,
|
||||
}
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
TextureDownloadState::Loaded(texture_handle) => {
|
||||
//need to show
|
||||
let mut size = texture_handle.size_vec2();
|
||||
clamp_to_width(&mut size, max_width);
|
||||
let image_button = ImageButton::new(texture_handle, size);
|
||||
if ui
|
||||
.add_sized(size, image_button)
|
||||
.on_hover_text(text)
|
||||
.clicked()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TextureDownloadState::Failed => {
|
||||
let button =
|
||||
ui.add_sized([max_width, max_width * image_type.ratio()], Button::new(text).wrap(true));
|
||||
if button.clicked() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if !path.exists() && url.is_none() {
|
||||
image_handles.insert(image_key, TextureDownloadState::Failed);
|
||||
} else {
|
||||
//We need to start a download
|
||||
//Redownload if file is too small
|
||||
if !path.exists()
|
||||
|| std::fs::metadata(path).map(|m| m.len()).unwrap_or_default() < 2
|
||||
{
|
||||
image_handles.insert(image_key.clone(), TextureDownloadState::Downloading);
|
||||
let to_download = ToDownload {
|
||||
path: path.to_path_buf(),
|
||||
url: url.unwrap().to_string(),
|
||||
app_name: "Thumbnail".to_string(),
|
||||
image_type: *image_type,
|
||||
};
|
||||
let image_handles = image_handles.clone();
|
||||
let image_key = image_key.clone();
|
||||
if let Some(rt) = rt {
|
||||
rt.spawn_blocking(move || {
|
||||
block_on(crate::steamgriddb::download_to_download(&to_download))
|
||||
.unwrap();
|
||||
image_handles.insert(image_key, TextureDownloadState::Downloaded);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
image_handles.insert(image_key.clone(), TextureDownloadState::Downloaded);
|
||||
}
|
||||
}
|
||||
impl GameButton {
|
||||
pub fn new(path: &Path) -> Self {
|
||||
Self {
|
||||
max_width: 200.0,
|
||||
path: path.to_path_buf(),
|
||||
text: Default::default(),
|
||||
image_type: ImageType::Grid,
|
||||
}
|
||||
}
|
||||
pub fn width(&mut self, max_width: f32) -> &mut Self {
|
||||
self.max_width = max_width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text(&mut self, text: &str) -> &mut Self {
|
||||
self.text = text.to_string();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn image_type(&mut self, image_type:&ImageType) -> &mut Self{
|
||||
self.image_type = *image_type;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn show_download(
|
||||
&self,
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
rt: &Runtime,
|
||||
url: &str,
|
||||
) -> bool {
|
||||
self.render_possible_image(ui, image_handles, Some(rt), Some(url))
|
||||
}
|
||||
|
||||
pub fn show(&self, ui: &mut egui::Ui, image_handles: &ImageHandles) -> bool {
|
||||
self.render_possible_image(ui, image_handles, None, None)
|
||||
}
|
||||
|
||||
fn render_possible_image(
|
||||
&self,
|
||||
ui: &mut egui::Ui,
|
||||
image_handles: &ImageHandles,
|
||||
rt: Option<&Runtime>,
|
||||
url: Option<&str>,
|
||||
) -> bool {
|
||||
{
|
||||
let path = &self.path;
|
||||
let image_key = path.to_string_lossy().to_string();
|
||||
match image_handles.get_mut(&image_key) {
|
||||
Some(mut state) => {
|
||||
match state.value() {
|
||||
TextureDownloadState::Downloading => {
|
||||
ui.ctx().request_repaint();
|
||||
//nothing to do,just wait
|
||||
ui.spinner();
|
||||
}
|
||||
TextureDownloadState::Downloaded => {
|
||||
//Need to load
|
||||
let image_data = load_image_from_path(path);
|
||||
match image_data {
|
||||
Ok(image_data) => {
|
||||
let handle = ui.ctx().load_texture(
|
||||
&image_key,
|
||||
image_data,
|
||||
egui::TextureOptions::LINEAR,
|
||||
);
|
||||
*state.value_mut() = TextureDownloadState::Loaded(handle);
|
||||
ui.spinner();
|
||||
}
|
||||
Err(_) => *state.value_mut() = TextureDownloadState::Failed,
|
||||
}
|
||||
ui.ctx().request_repaint();
|
||||
}
|
||||
TextureDownloadState::Loaded(texture_handle) => {
|
||||
//need to show
|
||||
let mut size = texture_handle.size_vec2();
|
||||
clamp_to_width(&mut size, self.max_width);
|
||||
let image_button = ImageButton::new(texture_handle, size);
|
||||
if ui
|
||||
.add_sized(size, image_button)
|
||||
.on_hover_text(&self.text)
|
||||
.clicked()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
TextureDownloadState::Failed => {
|
||||
let button = ui.add_sized(
|
||||
[self.max_width, self.max_width * self.image_type.ratio()],
|
||||
Button::new(&self.text).wrap(true),
|
||||
);
|
||||
if button.clicked() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
match url {
|
||||
Some(url) => {
|
||||
download_image(
|
||||
path,
|
||||
image_handles,
|
||||
&image_key,
|
||||
url,
|
||||
&self.image_type,
|
||||
rt,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
//Not possible to download
|
||||
if !path.exists() {
|
||||
image_handles.insert(image_key, TextureDownloadState::Failed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
impl Default for GameButton {
|
||||
fn default() -> Self {
|
||||
Self::new(Path::new(""))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn download_image(
|
||||
path: &Path,
|
||||
image_handles: &ImageHandles,
|
||||
image_key: &str,
|
||||
url: &str,
|
||||
image_type: &ImageType,
|
||||
rt: Option<&Runtime>,
|
||||
) {
|
||||
//We need to start a download
|
||||
//Redownload if file is too small
|
||||
if !path.exists() || std::fs::metadata(path).map(|m| m.len()).unwrap_or_default() < 2 {
|
||||
image_handles.insert(image_key.to_string(), TextureDownloadState::Downloading);
|
||||
let to_download = ToDownload {
|
||||
path: path.to_path_buf(),
|
||||
url: url.to_string(),
|
||||
app_name: "Thumbnail".to_string(),
|
||||
image_type: *image_type,
|
||||
};
|
||||
let image_handles = image_handles.clone();
|
||||
let image_key = image_key.to_string();
|
||||
if let Some(rt) = rt {
|
||||
rt.spawn_blocking(move || {
|
||||
match block_on(crate::steamgriddb::download_to_download(&to_download)) {
|
||||
Ok(_) => {
|
||||
image_handles.insert(image_key, TextureDownloadState::Downloaded);
|
||||
}
|
||||
Err(err) => {
|
||||
println!(
|
||||
"Failed downloading image {} error: {:?}",
|
||||
to_download.url, err
|
||||
);
|
||||
image_handles.insert(image_key, TextureDownloadState::Failed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
image_handles.insert(image_key.to_string(), TextureDownloadState::Downloaded);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,4 @@ mod steam_user_select;
|
||||
mod game_image_button;
|
||||
|
||||
pub use steam_user_select::render_user_select;
|
||||
pub use game_image_button::render_image_from_path;
|
||||
pub use game_image_button::render_image_from_path_or_url;
|
||||
pub use game_image_button::render_image_from_path_image_type;
|
||||
pub use game_image_button::GameButton;
|
||||
+4
-4
@@ -23,19 +23,19 @@ pub mod ui_images {
|
||||
pub const LOGO_ICON: &[u8] = include_bytes!("../../resources/logo_small.png");
|
||||
|
||||
pub fn get_import_image() -> ImageData {
|
||||
ImageData::Color(load_image_from_memory(IMPORT_GAMES_IMAGE).unwrap())
|
||||
ImageData::Color(load_image_from_memory(IMPORT_GAMES_IMAGE).unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn get_save_image() -> ImageData {
|
||||
ImageData::Color(load_image_from_memory(SAVE_IMAGE).unwrap())
|
||||
ImageData::Color(load_image_from_memory(SAVE_IMAGE).unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn get_logo() -> ImageData {
|
||||
ImageData::Color(load_image_from_memory(LOGO_32).unwrap())
|
||||
ImageData::Color(load_image_from_memory(LOGO_32).unwrap_or_default())
|
||||
}
|
||||
|
||||
pub fn get_logo_icon() -> IconData {
|
||||
let image = image::load_from_memory(LOGO_ICON).unwrap();
|
||||
let image = image::load_from_memory(LOGO_ICON).unwrap_or_default();
|
||||
let image_buffer = image.to_rgba8();
|
||||
let pixels = image_buffer.as_flat_samples();
|
||||
IconData {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{steam::SteamGameInfo};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GameType {
|
||||
Shortcut(ShortcutOwned),
|
||||
Shortcut(Box<ShortcutOwned>),
|
||||
SteamGame(SteamGameInfo),
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,15 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use steam_shortcuts_util::shortcut::ShortcutOwned;
|
||||
|
||||
use crate::{steamgriddb::ImageType, steam::SteamGameInfo};
|
||||
|
||||
use super::{gametype::GameType, constants::POSSIBLE_EXTENSIONS};
|
||||
use crate::{steam::SteamGameInfo, steamgriddb::ImageType};
|
||||
|
||||
use super::{constants::POSSIBLE_EXTENSIONS, gametype::GameType};
|
||||
|
||||
pub trait HasImageKey {
|
||||
|
||||
///Gives a unique key to an image given its type and user path
|
||||
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String);
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl HasImageKey for GameType {
|
||||
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
|
||||
match self {
|
||||
@@ -24,31 +20,29 @@ impl HasImageKey for GameType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl HasImageKey for SteamGameInfo {
|
||||
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
|
||||
let mut keys = POSSIBLE_EXTENSIONS
|
||||
.iter()
|
||||
.map(|ext| key_from_extension(self.appid, image_type, user_path, ext));
|
||||
let first = keys.next().unwrap();
|
||||
let other = keys.find(|(exsists, _, _)| *exsists);
|
||||
let (_, path, key) = other.unwrap_or(first);
|
||||
(path, key)
|
||||
let app_id = self.appid;
|
||||
key(app_id, image_type, user_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasImageKey for ShortcutOwned {
|
||||
fn key(&self, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
|
||||
let mut keys = POSSIBLE_EXTENSIONS
|
||||
.iter()
|
||||
.map(|ext| key_from_extension(self.app_id, image_type, user_path, ext));
|
||||
let first = keys.next().unwrap();
|
||||
let other = keys.find(|(exsists, _, _)| *exsists);
|
||||
let (_, path, key) = other.unwrap_or(first);
|
||||
(path, key)
|
||||
let app_id = self.app_id;
|
||||
key(app_id, image_type, user_path)
|
||||
}
|
||||
}
|
||||
|
||||
fn key(app_id: u32, image_type: &ImageType, user_path: &Path) -> (PathBuf, String) {
|
||||
let ext = |ext| key_from_extension(app_id, image_type, user_path, ext);
|
||||
let keys = POSSIBLE_EXTENSIONS.map(ext);
|
||||
let other = keys.iter().find(|(exsists, _, _)| *exsists);
|
||||
let first = ext(POSSIBLE_EXTENSIONS[0]);
|
||||
let (_, path, key) = other.unwrap_or(&first);
|
||||
(path.to_path_buf(), key.to_string())
|
||||
}
|
||||
|
||||
fn key_from_extension(
|
||||
app_id: u32,
|
||||
image_type: &ImageType,
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use egui::{Grid, };
|
||||
use egui::Grid;
|
||||
use futures::executor::block_on;
|
||||
use tokio::{ sync::watch};
|
||||
use tokio::sync::watch;
|
||||
|
||||
use crate::{
|
||||
steamgriddb::{get_image_extension, ImageType, ToDownload},
|
||||
ui::{
|
||||
components::GameButton,
|
||||
images::{
|
||||
constants::MAX_WIDTH,
|
||||
hasimagekey::HasImageKey,
|
||||
image_select_state::{ ImageSelectState},
|
||||
possible_image::PossibleImage,
|
||||
useraction::UserAction,
|
||||
constants::MAX_WIDTH, hasimagekey::HasImageKey, image_select_state::ImageSelectState,
|
||||
possible_image::PossibleImage, useraction::UserAction,
|
||||
},
|
||||
FetcStatus, MyEguiApp, components::render_image_from_path_or_url,
|
||||
FetcStatus, MyEguiApp,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -61,16 +59,10 @@ pub fn render_page_pick_image(
|
||||
.show(ui, |ui| {
|
||||
for image in images {
|
||||
let path = image.thumbnail_path.as_path();
|
||||
if render_image_from_path_or_url(
|
||||
ui,
|
||||
&state.image_handles,
|
||||
path,
|
||||
column_width,
|
||||
&image.full_url,
|
||||
image_type,
|
||||
&app.rt,
|
||||
&image.thumbnail_url,
|
||||
) {
|
||||
let mut button = GameButton::new(path);
|
||||
button.width(column_width);
|
||||
button.text("Pick image");
|
||||
if button.show_download(ui, &state.image_handles, &app.rt,&image.thumbnail_url) {
|
||||
return Some(image.clone());
|
||||
}
|
||||
column += 1;
|
||||
@@ -82,9 +74,9 @@ pub fn render_page_pick_image(
|
||||
None
|
||||
})
|
||||
.inner;
|
||||
if let Some(x) = x {
|
||||
if let Some(x) = x {
|
||||
return Some(UserAction::ImageSelected(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ui.horizontal(|ui| {
|
||||
@@ -99,76 +91,78 @@ pub fn render_page_pick_image(
|
||||
|
||||
pub fn handle_image_selected(app: &mut MyEguiApp, image: PossibleImage) {
|
||||
//We must have a user here
|
||||
let user = app.image_selected_state.steam_user.as_ref().unwrap();
|
||||
let selected_image_type = app
|
||||
.image_selected_state
|
||||
.image_type_selected
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let selected_shortcut = app.image_selected_state.selected_shortcut.as_ref().unwrap();
|
||||
let state = &app.image_selected_state;
|
||||
if let (Some(user), Some(selected_image_type), Some(selected_shortcut)) = (
|
||||
state.steam_user.as_ref(),
|
||||
state.image_type_selected.as_ref(),
|
||||
state.selected_shortcut.as_ref(),
|
||||
) {
|
||||
let get_image_extension = &get_image_extension(&image.mime);
|
||||
let ext = get_image_extension;
|
||||
let to_download_to_path = Path::new(&user.steam_user_data_folder)
|
||||
.join("config")
|
||||
.join("grid")
|
||||
.join(selected_image_type.file_name(selected_shortcut.app_id(), ext));
|
||||
|
||||
let ext = get_image_extension(&image.mime);
|
||||
let to_download_to_path = Path::new(&user.steam_user_data_folder)
|
||||
.join("config")
|
||||
.join("grid")
|
||||
.join(selected_image_type.file_name(selected_shortcut.app_id(), ext));
|
||||
delete_images_of_type(user, selected_shortcut, selected_image_type);
|
||||
|
||||
//Delete old possible images
|
||||
|
||||
let data_folder = Path::new(&user.steam_user_data_folder);
|
||||
|
||||
//Keep deleting images of this type untill we don't find any more
|
||||
let mut path = get_shortcut_image_path(app, data_folder);
|
||||
while Path::new(&path).exists() {
|
||||
let _ = std::fs::remove_file(&path);
|
||||
path = get_shortcut_image_path(app, data_folder);
|
||||
}
|
||||
|
||||
//Put the loaded thumbnail into the image handler map, we can use that for preview
|
||||
let full_image_key = to_download_to_path.to_string_lossy().to_string();
|
||||
let _ = app
|
||||
.image_selected_state
|
||||
.image_handles
|
||||
.remove(&full_image_key);
|
||||
let thumbnail_key = image.thumbnail_path.to_string_lossy().to_string();
|
||||
let thumbnail = app
|
||||
.image_selected_state
|
||||
.image_handles
|
||||
.remove(&thumbnail_key);
|
||||
if let Some((_key, thumbnail)) = thumbnail {
|
||||
app.image_selected_state
|
||||
//Put the loaded thumbnail into the image handler map, we can use that for preview
|
||||
let full_image_key = to_download_to_path.to_string_lossy().to_string();
|
||||
let _ = app
|
||||
.image_selected_state
|
||||
.image_handles
|
||||
.insert(full_image_key, thumbnail);
|
||||
}
|
||||
.remove(&full_image_key);
|
||||
let thumbnail_key = image.thumbnail_path.to_string_lossy().to_string();
|
||||
let thumbnail = app
|
||||
.image_selected_state
|
||||
.image_handles
|
||||
.remove(&thumbnail_key);
|
||||
if let Some((_key, thumbnail)) = thumbnail {
|
||||
app.image_selected_state
|
||||
.image_handles
|
||||
.insert(full_image_key, thumbnail);
|
||||
}
|
||||
|
||||
let app_name = selected_shortcut.name();
|
||||
let to_download = ToDownload {
|
||||
path: to_download_to_path,
|
||||
url: image.full_url.clone(),
|
||||
app_name: app_name.to_string(),
|
||||
image_type: *selected_image_type,
|
||||
};
|
||||
app.rt.spawn_blocking(move || {
|
||||
let _ = block_on(crate::steamgriddb::download_to_download(&to_download));
|
||||
});
|
||||
let app_name = selected_shortcut.name();
|
||||
let to_download = ToDownload {
|
||||
path: to_download_to_path,
|
||||
url: image.full_url.clone(),
|
||||
app_name: app_name.to_string(),
|
||||
image_type: *selected_image_type,
|
||||
};
|
||||
app.rt.spawn_blocking(move || {
|
||||
let _ = block_on(crate::steamgriddb::download_to_download(&to_download));
|
||||
});
|
||||
|
||||
clear_loaded_images(app);
|
||||
{
|
||||
app.image_selected_state.image_type_selected = None;
|
||||
app.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1;
|
||||
clear_loaded_images(app);
|
||||
{
|
||||
app.image_selected_state.image_type_selected = None;
|
||||
app.image_selected_state.image_options = watch::channel(FetcStatus::NeedsFetched).1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_shortcut_image_path(app: &MyEguiApp, data_folder: &Path) -> String {
|
||||
app.image_selected_state
|
||||
.selected_shortcut
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.key(
|
||||
&app.image_selected_state.image_type_selected.unwrap(),
|
||||
data_folder,
|
||||
)
|
||||
.1
|
||||
fn delete_images_of_type(
|
||||
user: &crate::steam::SteamUsersInfo,
|
||||
selected_shortcut: &crate::ui::images::gametype::GameType,
|
||||
selected_image_type: &ImageType,
|
||||
) {
|
||||
//Delete old possible images
|
||||
let data_folder = Path::new(&user.steam_user_data_folder);
|
||||
//Keep deleting images of this type untill we don't find any more
|
||||
let mut path = image_path(selected_shortcut, selected_image_type, data_folder);
|
||||
while path.exists() {
|
||||
let _ = std::fs::remove_file(path);
|
||||
path = image_path(selected_shortcut, selected_image_type, data_folder);
|
||||
}
|
||||
}
|
||||
|
||||
fn image_path(
|
||||
selected_shortcut: &crate::ui::images::gametype::GameType,
|
||||
selected_image_type: &ImageType,
|
||||
data_folder: &Path,
|
||||
) -> PathBuf {
|
||||
selected_shortcut.key(selected_image_type, data_folder).0
|
||||
}
|
||||
|
||||
fn clear_loaded_images(app: &mut MyEguiApp) {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use std::path::Path;
|
||||
|
||||
use crate::ui::components::GameButton;
|
||||
use crate::ui::images::{
|
||||
gametype::GameType, hasimagekey::HasImageKey, image_select_state::ImageSelectState,
|
||||
useraction::UserAction, ImageHandles,
|
||||
};
|
||||
use crate::{steamgriddb::ImageType, ui::components::render_image_from_path_image_type};
|
||||
use crate::{steamgriddb::ImageType};
|
||||
|
||||
const MAX_WIDTH: f32 = 300.;
|
||||
|
||||
@@ -12,59 +13,65 @@ pub fn render_page_shortcut_select_image_type(
|
||||
ui: &mut egui::Ui,
|
||||
state: &ImageSelectState,
|
||||
) -> Option<UserAction> {
|
||||
let shortcut = state.selected_shortcut.as_ref().unwrap();
|
||||
let user_path = &state.steam_user.as_ref().unwrap().steam_user_data_folder;
|
||||
|
||||
let thumbnail = |ui: &mut egui::Ui, image_type: &ImageType| {
|
||||
if render_thumbnail(ui, &state.image_handles, shortcut, image_type, user_path) {
|
||||
Some(UserAction::ImageTypeSelected(*image_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let x = if ui.available_width() > MAX_WIDTH * 3. {
|
||||
ui.horizontal(|ui| {
|
||||
let x = ui.vertical(|ui| thumbnail(ui, &ImageType::Grid)).inner;
|
||||
if x.is_some() {
|
||||
return x;
|
||||
let shortcut = &state.selected_shortcut.as_ref();
|
||||
let user_path = state
|
||||
.steam_user
|
||||
.as_ref()
|
||||
.map(|user| &user.steam_user_data_folder);
|
||||
if let (Some(shortcut), Some(user_path)) = (shortcut, user_path) {
|
||||
let thumbnail = |ui: &mut egui::Ui, image_type: &ImageType| {
|
||||
if render_thumbnail(ui, &state.image_handles, shortcut, image_type, user_path) {
|
||||
Some(UserAction::ImageTypeSelected(*image_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let x = ui
|
||||
.vertical(|ui| {
|
||||
let types = &[ImageType::Hero, ImageType::WideGrid, ImageType::Logo];
|
||||
};
|
||||
let x = if ui.available_width() > MAX_WIDTH * 3. {
|
||||
ui.horizontal(|ui| {
|
||||
let x = ui.vertical(|ui| thumbnail(ui, &ImageType::Grid)).inner;
|
||||
if x.is_some() {
|
||||
return x;
|
||||
}
|
||||
let x = ui
|
||||
.vertical(|ui| {
|
||||
let types = &[ImageType::Hero, ImageType::WideGrid, ImageType::Logo];
|
||||
types
|
||||
.iter()
|
||||
.flat_map(|image_type| thumbnail(ui, image_type))
|
||||
.next()
|
||||
})
|
||||
.inner;
|
||||
if x.is_some() {
|
||||
return x;
|
||||
}
|
||||
ui.vertical(|ui| {
|
||||
let types = &[ImageType::Icon, ImageType::BigPicture];
|
||||
types
|
||||
.iter()
|
||||
.flat_map(|image_type| thumbnail(ui, image_type))
|
||||
.next()
|
||||
})
|
||||
.inner;
|
||||
if x.is_some() {
|
||||
return x;
|
||||
}
|
||||
ui.vertical(|ui| {
|
||||
let types = &[ImageType::Icon, ImageType::BigPicture];
|
||||
types
|
||||
.iter()
|
||||
.flat_map(|image_type| thumbnail(ui, image_type))
|
||||
.next()
|
||||
.inner
|
||||
})
|
||||
.inner
|
||||
})
|
||||
.inner
|
||||
} else {
|
||||
let types = ImageType::all();
|
||||
types
|
||||
.iter()
|
||||
.flat_map(|image_type| thumbnail(ui, image_type))
|
||||
.next()
|
||||
};
|
||||
} else {
|
||||
let types = ImageType::all();
|
||||
types
|
||||
.iter()
|
||||
.flat_map(|image_type| thumbnail(ui, image_type))
|
||||
.next()
|
||||
};
|
||||
|
||||
if ui
|
||||
.button("Click here if the images are for a wrong game")
|
||||
.clicked()
|
||||
{
|
||||
return Some(UserAction::CorrectGridId);
|
||||
if ui
|
||||
.button("Click here if the images are for a wrong game")
|
||||
.clicked()
|
||||
{
|
||||
return Some(UserAction::CorrectGridId);
|
||||
}
|
||||
x
|
||||
} else {
|
||||
None
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
fn render_thumbnail(
|
||||
@@ -76,12 +83,10 @@ fn render_thumbnail(
|
||||
) -> bool {
|
||||
let (path, _key) = shortcut.key(image_type, Path::new(&user_path));
|
||||
let text = format!("Pick {} image", image_type.name());
|
||||
render_image_from_path_image_type(
|
||||
ui,
|
||||
image_handles,
|
||||
path.as_path(),
|
||||
MAX_WIDTH,
|
||||
&text,
|
||||
image_type,
|
||||
)
|
||||
let mut image = GameButton::new(&path);
|
||||
image
|
||||
.width(MAX_WIDTH)
|
||||
.text(&text)
|
||||
.image_type(image_type)
|
||||
.show(ui, image_handles)
|
||||
}
|
||||
|
||||
@@ -4,27 +4,30 @@ use steam_shortcuts_util::shortcut::ShortcutOwned;
|
||||
|
||||
use crate::{
|
||||
steam::SteamUsersInfo,
|
||||
steamgriddb::{ImageType, CachedSearch},
|
||||
steamgriddb::{CachedSearch, ImageType},
|
||||
ui::{
|
||||
images::{
|
||||
gametype::GameType, hasimagekey::HasImageKey,
|
||||
texturestate::TextureDownloadState, useraction::UserAction,
|
||||
gametype::GameType, hasimagekey::HasImageKey, texturestate::TextureDownloadState,
|
||||
useraction::UserAction,
|
||||
},
|
||||
MyEguiApp, ui_images::load_image_from_path, components::render_image_from_path,
|
||||
ui_images::load_image_from_path,
|
||||
MyEguiApp, components::GameButton,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui) -> Option<UserAction> {
|
||||
pub fn render_page_shortcut_images_overview(
|
||||
app: &MyEguiApp,
|
||||
ui: &mut egui::Ui,
|
||||
) -> Option<UserAction> {
|
||||
let user_info = &app.image_selected_state.steam_user;
|
||||
let shortcuts = &app.image_selected_state.user_shortcuts;
|
||||
|
||||
let width = ui.available_size().x;
|
||||
let column_width = 100.;
|
||||
let column_padding = 23.;
|
||||
let columns = (width / (column_width + column_padding)).floor() as u32;
|
||||
let mut cur_column = 0;
|
||||
match shortcuts {
|
||||
Some(shortcuts) => {
|
||||
let user_info = &app.image_selected_state.steam_user.as_ref().unwrap();
|
||||
match (user_info, shortcuts) {
|
||||
(Some(user_info), Some(shortcuts)) => {
|
||||
if let Some(action) = egui::Grid::new("ui_images")
|
||||
.show(ui, |ui| {
|
||||
for shortcut in shortcuts {
|
||||
@@ -46,7 +49,7 @@ pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui)
|
||||
return action;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
_ => {
|
||||
ui.label("Could not find any shortcuts");
|
||||
}
|
||||
}
|
||||
@@ -56,7 +59,7 @@ pub fn render_page_shortcut_images_overview(app: &MyEguiApp, ui: &mut egui::Ui)
|
||||
fn render_image(
|
||||
app: &MyEguiApp,
|
||||
shortcut: &ShortcutOwned,
|
||||
user_info: &&SteamUsersInfo,
|
||||
user_info: &SteamUsersInfo,
|
||||
column_width: f32,
|
||||
ui: &mut egui::Ui,
|
||||
) -> Option<Option<UserAction>> {
|
||||
@@ -65,18 +68,21 @@ fn render_image(
|
||||
Path::new(&user_info.steam_user_data_folder),
|
||||
);
|
||||
|
||||
let clicked= render_image_from_path(ui, &app.image_selected_state.image_handles, Path::new(&key), column_width, &shortcut.app_name);
|
||||
let mut button = GameButton::new(Path::new(&key));
|
||||
button.text(&shortcut.app_name);
|
||||
button.width(column_width);
|
||||
let clicked = button.show(ui, &app.image_selected_state.image_handles);
|
||||
if clicked {
|
||||
return Some(Some(UserAction::ShortcutSelected(GameType::Shortcut(
|
||||
shortcut.clone(),
|
||||
Box::new(shortcut.clone()),
|
||||
))));
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn handle_shortcut_selected(app: &mut MyEguiApp, shortcut: GameType, ui: &mut egui::Ui) {
|
||||
let state = &mut app.image_selected_state;
|
||||
//We must have a user to make see this action;
|
||||
let user = state.steam_user.as_ref().unwrap();
|
||||
let state = &mut app.image_selected_state;
|
||||
//We must have a user to make see this action;
|
||||
if let Some(user) = state.steam_user.as_ref() {
|
||||
if let Some(auth_key) = &app.settings.steamgrid_db.auth_key {
|
||||
let client = steamgriddb_api::Client::new(auth_key);
|
||||
let search = CachedSearch::new(&client);
|
||||
@@ -102,3 +108,4 @@ pub fn handle_shortcut_selected(app: &mut MyEguiApp, shortcut: GameType, ui: &mu
|
||||
}
|
||||
state.selected_shortcut = Some(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use super::{
|
||||
hasimagekey::HasImageKey,
|
||||
image_select_state::ImageSelectState,
|
||||
pages::{
|
||||
handle_correct_grid_request, handle_grid_change, handle_shortcut_selected,
|
||||
render_page_pick_image, render_page_shortcut_images_overview,
|
||||
render_page_shortcut_select_image_type, render_page_steam_images_overview, handle_image_selected,
|
||||
handle_correct_grid_request, handle_grid_change, handle_image_selected,
|
||||
handle_shortcut_selected, render_page_pick_image, render_page_shortcut_images_overview,
|
||||
render_page_shortcut_select_image_type, render_page_steam_images_overview,
|
||||
},
|
||||
possible_image::PossibleImage,
|
||||
texturestate::TextureDownloadState,
|
||||
@@ -157,7 +157,7 @@ impl MyEguiApp {
|
||||
self.handle_image_type_selected(image_type);
|
||||
}
|
||||
UserAction::ImageSelected(image) => {
|
||||
handle_image_selected(self,image);
|
||||
handle_image_selected(self, image);
|
||||
}
|
||||
UserAction::BackButton => {
|
||||
self.handle_back_button_action();
|
||||
@@ -197,11 +197,13 @@ impl MyEguiApp {
|
||||
.image_selected_state
|
||||
.selected_shortcut
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.app_id();
|
||||
self.settings
|
||||
.steamgrid_db
|
||||
.set_image_banned(&image_type, app_id, should_ban);
|
||||
.map(|m| m.app_id());
|
||||
if let Some(app_id) = app_id {
|
||||
self.settings
|
||||
.steamgrid_db
|
||||
.set_image_banned(&image_type, app_id, should_ban);
|
||||
}
|
||||
|
||||
self.handle_image_type_clear(image_type);
|
||||
}
|
||||
|
||||
@@ -222,38 +224,39 @@ impl MyEguiApp {
|
||||
self.rt.spawn_blocking(move || {
|
||||
let task = download_images(&settings, &users, &mut sender_op);
|
||||
block_on(task);
|
||||
let _ = sender_op.unwrap().send(SyncProgress::Done);
|
||||
if let Some(sender_op) = sender_op {
|
||||
let _ = sender_op.send(SyncProgress::Done);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_image_type_clear(&mut self, image_type: ImageType) {
|
||||
let app_id = self
|
||||
.image_selected_state
|
||||
.selected_shortcut
|
||||
.as_ref()
|
||||
.map(|s| s.app_id());
|
||||
let data_folder = &self
|
||||
.image_selected_state
|
||||
.steam_user
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.steam_user_data_folder;
|
||||
for ext in POSSIBLE_EXTENSIONS {
|
||||
let file_name = image_type.file_name(
|
||||
self.image_selected_state
|
||||
.selected_shortcut
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.app_id(),
|
||||
ext,
|
||||
);
|
||||
let path = Path::new(data_folder)
|
||||
.join("config")
|
||||
.join("grid")
|
||||
.join(file_name);
|
||||
if path.exists() {
|
||||
let _ = std::fs::remove_file(&path);
|
||||
.map(|s| &s.steam_user_data_folder);
|
||||
if let (Some(app_id), Some(data_folder)) = (app_id, data_folder) {
|
||||
for ext in POSSIBLE_EXTENSIONS {
|
||||
let file_name = image_type.file_name(app_id, ext);
|
||||
let path = Path::new(data_folder)
|
||||
.join("config")
|
||||
.join("grid")
|
||||
.join(file_name);
|
||||
if path.exists() {
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
let key = path.to_string_lossy().to_string();
|
||||
self.image_selected_state.image_handles.remove(&key);
|
||||
}
|
||||
let key = path.to_string_lossy().to_string();
|
||||
self.image_selected_state.image_handles.remove(&key);
|
||||
self.image_selected_state.image_type_selected = None;
|
||||
}
|
||||
self.image_selected_state.image_type_selected = None;
|
||||
}
|
||||
|
||||
fn handle_set_game_mode(&mut self, game_mode: GameMode) {
|
||||
@@ -304,11 +307,6 @@ impl MyEguiApp {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn handle_back_button_action(&mut self) {
|
||||
let state = &mut self.image_selected_state;
|
||||
if state.possible_names.is_some() {
|
||||
@@ -331,28 +329,35 @@ fn load_image_grids(
|
||||
ui: &mut egui::Ui,
|
||||
) -> Vec<ShortcutOwned> {
|
||||
let user_info = crate::steam::get_shortcuts_for_user(user);
|
||||
let mut user_folder = user_info.path.clone();
|
||||
user_folder.pop();
|
||||
user_folder.pop();
|
||||
let mut shortcuts = user_info.shortcuts;
|
||||
shortcuts.sort_by_key(|s| s.app_name.clone());
|
||||
let image_type = &ImageType::Grid;
|
||||
for shortcut in &shortcuts {
|
||||
let (path, key) = shortcut.key(image_type, &user_folder);
|
||||
let loaded = state.image_handles.contains_key(&key);
|
||||
if !loaded && path.exists() {
|
||||
let image = load_image_from_path(&path);
|
||||
if let Ok(image) = image {
|
||||
let texture = ui
|
||||
.ctx()
|
||||
.load_texture(&key, image, egui::TextureOptions::LINEAR);
|
||||
state
|
||||
.image_handles
|
||||
.insert(key, TextureDownloadState::Loaded(texture));
|
||||
match user_info {
|
||||
Ok(user_info) => {
|
||||
let mut user_folder = user_info.path.clone();
|
||||
user_folder.pop();
|
||||
user_folder.pop();
|
||||
let mut shortcuts = user_info.shortcuts;
|
||||
shortcuts.sort_by_key(|s| s.app_name.clone());
|
||||
let image_type = &ImageType::Grid;
|
||||
for shortcut in &shortcuts {
|
||||
let (path, key) = shortcut.key(image_type, &user_folder);
|
||||
let loaded = state.image_handles.contains_key(&key);
|
||||
if !loaded && path.exists() {
|
||||
let image = load_image_from_path(&path);
|
||||
if let Ok(image) = image {
|
||||
let texture =
|
||||
ui.ctx()
|
||||
.load_texture(&key, image, egui::TextureOptions::LINEAR);
|
||||
state
|
||||
.image_handles
|
||||
.insert(key, TextureDownloadState::Loaded(texture));
|
||||
}
|
||||
}
|
||||
}
|
||||
shortcuts
|
||||
}
|
||||
Err(_err) => {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
shortcuts
|
||||
}
|
||||
|
||||
fn render_shortcut_mode_select(state: &ImageSelectState, ui: &mut egui::Ui) -> Option<UserAction> {
|
||||
|
||||
+30
-11
@@ -62,14 +62,23 @@ impl MyEguiApp {
|
||||
}
|
||||
|
||||
pub fn restore_backup(steam_settings: &SteamSettings, shortcut_path: &Path) -> bool {
|
||||
let file_name = shortcut_path.file_name().unwrap();
|
||||
let file_name = shortcut_path.file_name();
|
||||
let paths = get_shortcuts_paths(steam_settings);
|
||||
if let Ok(paths) = paths {
|
||||
if let (Ok(paths), Some(file_name)) = (paths, file_name) {
|
||||
for user in paths {
|
||||
if let Some(user_shortcut_path) = user.shortcut_path {
|
||||
if file_name.to_string_lossy().starts_with(&user.user_id) {
|
||||
std::fs::copy(shortcut_path, Path::new(&user_shortcut_path)).unwrap();
|
||||
println!("Restored shortcut to path : {}", user_shortcut_path);
|
||||
match std::fs::copy(shortcut_path, Path::new(&user_shortcut_path)) {
|
||||
Ok(_) => {
|
||||
println!("Restored shortcut to path : {}", user_shortcut_path);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"Failed to restored shortcut to path : {} gave error: {:?}",
|
||||
user_shortcut_path, err
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -100,7 +109,7 @@ pub fn load_backups() -> Vec<PathBuf> {
|
||||
result
|
||||
}
|
||||
|
||||
const DATE_FORMAT :&str = "[year]-[month]-[day]-[hour]-[minute]-[second]";
|
||||
const DATE_FORMAT: &str = "[year]-[month]-[day]-[hour]-[minute]-[second]";
|
||||
|
||||
pub fn backup_shortcuts(steam_settings: &SteamSettings) {
|
||||
use time::OffsetDateTime;
|
||||
@@ -108,18 +117,28 @@ pub fn backup_shortcuts(steam_settings: &SteamSettings) {
|
||||
let backup_folder = get_backups_flder();
|
||||
let paths = get_shortcuts_paths(steam_settings);
|
||||
let date = OffsetDateTime::now_utc();
|
||||
let format = format_description::parse(DATE_FORMAT).unwrap();
|
||||
let date_string = date.format(&format).unwrap();
|
||||
if let Ok(user_infos) = paths {
|
||||
let format = format_description::parse(DATE_FORMAT);
|
||||
if let Ok(format) = format{
|
||||
let date_string = date.format(&format);
|
||||
if let (Ok(date_string),Ok(user_infos)) = (date_string,paths) {
|
||||
for user_info in user_infos {
|
||||
if let Some(shortcut_path) = user_info.shortcut_path {
|
||||
let new_path = backup_folder.join(format!(
|
||||
"{}-{}-shortcuts.vdf",
|
||||
user_info.user_id, date_string
|
||||
));
|
||||
println!("Backed up shortcut at: {:?}", new_path);
|
||||
std::fs::copy(shortcut_path, &new_path).unwrap();
|
||||
match std::fs::copy(shortcut_path, &new_path) {
|
||||
Ok(_) => {
|
||||
println!("Backed up shortcut at: {:?}", new_path);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"Failed to backup shortcut at: {:?}, error: {:?}",
|
||||
new_path, err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ impl MyEguiApp {
|
||||
let mut user_info = vec![];
|
||||
for user in users {
|
||||
let shortcut_info = get_shortcuts_for_user(&user);
|
||||
user_info.push(shortcut_info);
|
||||
if let Ok(shortcut_info) = shortcut_info {
|
||||
user_info.push(shortcut_info);
|
||||
}
|
||||
}
|
||||
user_info
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ use futures::executor::block_on;
|
||||
|
||||
use steam_shortcuts_util::shortcut::ShortcutOwned;
|
||||
use tokio::sync::watch;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use crate::config::get_renames_file;
|
||||
use crate::platforms::ShortcutToImport;
|
||||
@@ -123,7 +124,14 @@ impl MyEguiApp {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn run_sync(&mut self, wait: bool) {
|
||||
pub fn run_sync_blocking(&mut self) -> eyre::Result<()> {
|
||||
self.run_sync(true)
|
||||
}
|
||||
|
||||
pub fn run_sync_async(&mut self) {
|
||||
let _ = self.run_sync(false);
|
||||
}
|
||||
fn run_sync(&mut self, wait: bool) -> eyre::Result<()> {
|
||||
let (sender, reciever) = watch::channel(SyncProgress::NotStarted);
|
||||
let settings = self.settings.clone();
|
||||
if settings.steam.stop_steam {
|
||||
@@ -136,7 +144,7 @@ impl MyEguiApp {
|
||||
let _ = sender.send(SyncProgress::Starting);
|
||||
if all_ready {
|
||||
let shortcuts_to_import = get_all_games(&self.games_to_sync);
|
||||
let handle = self.rt.spawn_blocking(move || {
|
||||
let handle: JoinHandle<eyre::Result<()>> = self.rt.spawn_blocking(move || {
|
||||
#[cfg(target_family = "unix")]
|
||||
setup_proton(shortcuts_to_import.iter());
|
||||
|
||||
@@ -145,12 +153,11 @@ impl MyEguiApp {
|
||||
let mut some_sender = Some(sender);
|
||||
backup_shortcuts(&settings.steam);
|
||||
let usersinfo =
|
||||
sync::sync_shortcuts(&settings, &import_games, &mut some_sender, &renames)
|
||||
.unwrap();
|
||||
sync::sync_shortcuts(&settings, &import_games, &mut some_sender, &renames)?;
|
||||
let task = download_images(&settings, &usersinfo, &mut some_sender);
|
||||
block_on(task);
|
||||
//Run a second time to fix up shortcuts after images are downloaded
|
||||
if let Err(e) = sync::fix_all_shortcut_icons(&settings){
|
||||
if let Err(e) = sync::fix_all_shortcut_icons(&settings) {
|
||||
eprintln!("Could not fix shortcuts with error {e}");
|
||||
}
|
||||
|
||||
@@ -160,11 +167,13 @@ impl MyEguiApp {
|
||||
if settings.steam.start_steam {
|
||||
crate::steam::ensure_steam_started(&settings.steam);
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
if wait {
|
||||
self.rt.block_on(handle).unwrap();
|
||||
self.rt.block_on(handle)??;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,6 +211,8 @@ where
|
||||
crate::sync::symlinks::create_sym_links(&shortcut_info.shortcut);
|
||||
}
|
||||
}
|
||||
setup_proton_games(&shortcuts_to_proton);
|
||||
if let Err(err) = setup_proton_games(&shortcuts_to_proton){
|
||||
eprintln!("failed to save proton settings: {:?}",err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-20
@@ -15,13 +15,14 @@ use crate::{
|
||||
};
|
||||
|
||||
use super::{
|
||||
images::ImageSelectState,
|
||||
ui_colors::{
|
||||
BACKGROUND_COLOR, BG_STROKE_COLOR, EXTRA_BACKGROUND_COLOR, LIGHT_ORANGE, ORANGE, PURLPLE,
|
||||
TEXT_COLOR,
|
||||
},
|
||||
ui_images::{get_import_image, get_logo, get_logo_icon, get_save_image},
|
||||
ui_import_games::FetcStatus,
|
||||
BackupState, DiconnectState, images::ImageSelectState,
|
||||
BackupState, DiconnectState,
|
||||
};
|
||||
|
||||
const SECTION_SPACING: f32 = 25.0;
|
||||
@@ -70,12 +71,12 @@ pub struct MyEguiApp {
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
pub fn new() -> Self {
|
||||
let mut runtime = Runtime::new().unwrap();
|
||||
let settings = Settings::new().expect("We must be able to load our settings");
|
||||
pub fn new() -> eyre::Result<Self> {
|
||||
let mut runtime = Runtime::new()?;
|
||||
let settings = Settings::new()?;
|
||||
let platforms = get_platforms();
|
||||
let games_to_sync = create_games_to_sync(&mut runtime, &platforms);
|
||||
Self {
|
||||
Ok(Self {
|
||||
selected_menu: Menues::Import,
|
||||
settings,
|
||||
rt: runtime,
|
||||
@@ -88,7 +89,7 @@ impl MyEguiApp {
|
||||
rename_map: get_rename_map(),
|
||||
current_edit: Option::None,
|
||||
platforms,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn render_import_button(&mut self, ui: &mut egui::Ui) {
|
||||
@@ -121,17 +122,19 @@ impl MyEguiApp {
|
||||
let texture = self.get_import_image(ui);
|
||||
let size = texture.size_vec2();
|
||||
let image_button = ImageButton::new(texture, size * 0.40);
|
||||
if all_ready && !syncing{
|
||||
if all_ready && !syncing {
|
||||
if ui
|
||||
.add(image_button)
|
||||
.on_hover_text("Import your games into steam")
|
||||
.clicked(){
|
||||
save_settings(&self.settings, &self.platforms);
|
||||
self.run_sync(false);
|
||||
.clicked()
|
||||
{
|
||||
if let Err(err) = save_settings(&self.settings, &self.platforms){
|
||||
eprintln!("Failed to save settings {:?}",err);
|
||||
}
|
||||
}else{
|
||||
ui
|
||||
.add(image_button)
|
||||
self.run_sync_async();
|
||||
}
|
||||
} else {
|
||||
ui.add(image_button)
|
||||
.on_hover_text("Waiting for sync to finish");
|
||||
}
|
||||
}
|
||||
@@ -265,7 +268,9 @@ impl App for MyEguiApp {
|
||||
let save_button = ImageButton::new(texture, size * 0.5);
|
||||
|
||||
if ui.add(save_button).on_hover_text("Save settings").clicked() {
|
||||
save_settings(&self.settings, &self.platforms);
|
||||
if let Err(err) = save_settings(&self.settings, &self.platforms){
|
||||
eprintln!("Failed to save settings: {:?}",err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -341,17 +346,17 @@ fn setup(ctx: &egui::Context) {
|
||||
create_style(&mut style);
|
||||
ctx.set_style(style);
|
||||
}
|
||||
pub fn run_sync() {
|
||||
let mut app = MyEguiApp::new();
|
||||
pub fn run_sync() -> eyre::Result<()>{
|
||||
let mut app = MyEguiApp::new()?;
|
||||
while !all_ready(&app.games_to_sync) {
|
||||
println!("Finding games, trying again in 500ms");
|
||||
std::thread::sleep(Duration::from_secs_f32(0.5));
|
||||
}
|
||||
app.run_sync(true);
|
||||
app.run_sync_blocking()
|
||||
}
|
||||
|
||||
pub fn run_ui(args: Vec<String>) {
|
||||
let app = MyEguiApp::new();
|
||||
pub fn run_ui(args: Vec<String>) -> eyre::Result<()>{
|
||||
let app = MyEguiApp::new()?;
|
||||
let no_v_sync = args.contains(&"--no-vsync".to_string());
|
||||
let fullscreen = is_fullscreen(&args);
|
||||
let native_options = eframe::NativeOptions {
|
||||
@@ -370,6 +375,7 @@ pub fn run_ui(args: Vec<String>) {
|
||||
Box::new(app)
|
||||
}),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_fullscreen(args: &[String]) -> bool {
|
||||
@@ -377,5 +383,5 @@ fn is_fullscreen(args: &[String]) -> bool {
|
||||
Ok(value) => !value.is_empty(),
|
||||
Err(_) => false,
|
||||
};
|
||||
is_steam_mode || args.contains(&"--fullscreen".to_string())
|
||||
is_steam_mode || args.contains(&"--fullscreen".to_string())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user