mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
Merge branch 'main' of https://github.com/PhilipK/steam_shortcuts_sync
This commit is contained in:
@@ -26,7 +26,7 @@ impl From<ManifestItem> for ShortcutOwned {
|
|||||||
manifest.install_location, manifest.launch_executable
|
manifest.install_location, manifest.launch_executable
|
||||||
);
|
);
|
||||||
let mut start_dir = manifest.install_location.clone();
|
let mut start_dir = manifest.install_location.clone();
|
||||||
if !manifest.install_location.starts_with("\"") {
|
if !manifest.install_location.starts_with('"') {
|
||||||
start_dir = format!("\"{}\"", manifest.install_location);
|
start_dir = format!("\"{}\"", manifest.install_location);
|
||||||
}
|
}
|
||||||
let shortcut = Shortcut::new(
|
let shortcut = Shortcut::new(
|
||||||
|
|||||||
+4
-10
@@ -54,8 +54,7 @@ impl Platform<GogShortcut, GogErrors> for GogPlatform {
|
|||||||
if path.exists() {
|
if path.exists() {
|
||||||
let dirs = path.read_dir();
|
let dirs = path.read_dir();
|
||||||
if let Ok(dirs) = dirs {
|
if let Ok(dirs) = dirs {
|
||||||
for dir in dirs {
|
for dir in dirs.flatten() {
|
||||||
if let Ok(dir) = dir {
|
|
||||||
if let Ok(file_type) = dir.file_type() {
|
if let Ok(file_type) = dir.file_type() {
|
||||||
if file_type.is_dir() {
|
if file_type.is_dir() {
|
||||||
game_folders.push(dir.path());
|
game_folders.push(dir.path());
|
||||||
@@ -65,21 +64,17 @@ impl Platform<GogShortcut, GogErrors> for GogPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let mut games = vec![];
|
let mut games = vec![];
|
||||||
for game_folder in &game_folders {
|
for game_folder in &game_folders {
|
||||||
if let Ok(files) = game_folder.read_dir() {
|
if let Ok(files) = game_folder.read_dir() {
|
||||||
for file in files {
|
for file in files.flatten() {
|
||||||
if let Ok(file) = file {
|
|
||||||
if let Some(file_name) = file.file_name().to_str() {
|
if let Some(file_name) = file.file_name().to_str() {
|
||||||
if file_name.starts_with("goggame-") {
|
if file_name.starts_with("goggame-") {
|
||||||
if let Some(extension) = file.path().extension() {
|
if let Some(extension) = file.path().extension() {
|
||||||
if let Some(extension) = extension.to_str() {
|
if let Some(extension) = extension.to_str() {
|
||||||
if extension == "info" {
|
if extension == "info" {
|
||||||
// Finally we know we can parse this as a game
|
// Finally we know we can parse this as a game
|
||||||
if let Ok(content) =
|
if let Ok(content) = std::fs::read_to_string(file.path()) {
|
||||||
std::fs::read_to_string(file.path())
|
|
||||||
{
|
|
||||||
if let Ok(gog_game) =
|
if let Ok(gog_game) =
|
||||||
serde_json::from_str::<GogGame>(&content)
|
serde_json::from_str::<GogGame>(&content)
|
||||||
{
|
{
|
||||||
@@ -94,7 +89,6 @@ impl Platform<GogShortcut, GogErrors> for GogPlatform {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let mut shortcuts = vec![];
|
let mut shortcuts = vec![];
|
||||||
for (game, game_folder) in games {
|
for (game, game_folder) in games {
|
||||||
@@ -114,7 +108,7 @@ impl Platform<GogShortcut, GogErrors> for GogPlatform {
|
|||||||
Some(working_dir) => game_folder
|
Some(working_dir) => game_folder
|
||||||
.join(working_dir)
|
.join(working_dir)
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap_or(folder_path.as_str())
|
.unwrap_or_else(|| folder_path.as_str())
|
||||||
.to_string(),
|
.to_string(),
|
||||||
None => folder_path.to_string(),
|
None => folder_path.to_string(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ use super::butler_db_parser::*;
|
|||||||
use super::receipt::Receipt;
|
use super::receipt::Receipt;
|
||||||
use super::{ItchGame, ItchSettings};
|
use super::{ItchGame, ItchSettings};
|
||||||
use crate::platform::Platform;
|
use crate::platform::Platform;
|
||||||
use _core::iter::FromIterator;
|
|
||||||
use failure::*;
|
use failure::*;
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
@@ -50,7 +49,7 @@ impl Platform<ItchGame, ItchErrors> for ItchPlatform {
|
|||||||
}?;
|
}?;
|
||||||
|
|
||||||
//This is done to remove douplicates
|
//This is done to remove douplicates
|
||||||
let paths: HashSet<&DbPaths> = HashSet::from_iter(paths.iter());
|
let paths: HashSet<&DbPaths> = paths.iter().collect();
|
||||||
|
|
||||||
let res = paths.iter().filter_map(|e| dbpath_to_game(*e)).collect();
|
let res = paths.iter().filter_map(|e| dbpath_to_game(*e)).collect();
|
||||||
Ok(res)
|
Ok(res)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ impl From<LegendaryGame> for ShortcutOwned {
|
|||||||
let exe = format!("\"{}\\{}\"", game.install_path, game.executable);
|
let exe = format!("\"{}\\{}\"", game.install_path, game.executable);
|
||||||
let launch = format!("legendary launch {}", game.app_name);
|
let launch = format!("legendary launch {}", game.app_name);
|
||||||
let mut start_dir = game.install_path.clone();
|
let mut start_dir = game.install_path.clone();
|
||||||
if !game.install_path.starts_with("\"") {
|
if !game.install_path.starts_with('"') {
|
||||||
start_dir = format!("\"{}\"", game.install_path);
|
start_dir = format!("\"{}\"", game.install_path);
|
||||||
}
|
}
|
||||||
let shortcut = Shortcut::new(
|
let shortcut = Shortcut::new(
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ impl Platform<OriginGame, OriginErrors> for OriginPlatform {
|
|||||||
.settings
|
.settings
|
||||||
.path
|
.path
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| get_default_location()),
|
.unwrap_or_else(get_default_location),
|
||||||
)
|
)
|
||||||
.join("LocalContent");
|
.join("LocalContent");
|
||||||
if !origin_folder.exists() {
|
if !origin_folder.exists() {
|
||||||
@@ -59,7 +59,7 @@ impl Platform<OriginGame, OriginErrors> for OriginPlatform {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
id.map(|id| OriginGame {
|
id.map(|id| OriginGame {
|
||||||
id: id.to_string(),
|
id,
|
||||||
title: game_title,
|
title: game_title,
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@@ -72,8 +72,7 @@ fn get_folder_mfst_file_content(game_folder_path: &Path) -> Option<String> {
|
|||||||
if let Ok(game_folder_files) = game_folder_files {
|
if let Ok(game_folder_files) = game_folder_files {
|
||||||
let mfst_file = game_folder_files
|
let mfst_file = game_folder_files
|
||||||
.filter_map(|file| file.ok())
|
.filter_map(|file| file.ok())
|
||||||
.filter(is_mfst_file)
|
.find(is_mfst_file)
|
||||||
.next()
|
|
||||||
.map(|file| std::fs::read_to_string(&file.path()));
|
.map(|file| std::fs::read_to_string(&file.path()));
|
||||||
return match mfst_file {
|
return match mfst_file {
|
||||||
Some(mfst_file) => mfst_file.ok(),
|
Some(mfst_file) => mfst_file.ok(),
|
||||||
@@ -99,7 +98,6 @@ fn parse_id_from_file(i: &str) -> nom::IResult<&str, &str> {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_default_location() -> String {
|
pub fn get_default_location() -> String {
|
||||||
|
|
||||||
//TODO implement this for linux:
|
//TODO implement this for linux:
|
||||||
// https://www.toptensoftware.com/blog/running-ea-origin-games-under-linux-via-steam-and-proton/
|
// https://www.toptensoftware.com/blog/running-ea-origin-games-under-linux-via-steam-and-proton/
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use super::CachedSearch;
|
|||||||
|
|
||||||
const CONCURRENT_REQUESTS: usize = 10;
|
const CONCURRENT_REQUESTS: usize = 10;
|
||||||
|
|
||||||
pub async fn download_images_for_users<'b>(settings: &Settings, users: &Vec<SteamUsersInfo>) {
|
pub async fn download_images_for_users<'b>(settings: &Settings, users: &[SteamUsersInfo]) {
|
||||||
let auth_key = &settings.steamgrid_db.auth_key;
|
let auth_key = &settings.steamgrid_db.auth_key;
|
||||||
if let Some(auth_key) = auth_key {
|
if let Some(auth_key) = auth_key {
|
||||||
println!("Checking for game images");
|
println!("Checking for game images");
|
||||||
@@ -51,7 +51,7 @@ pub async fn download_images_for_users<'b>(settings: &Settings, users: &Vec<Stea
|
|||||||
|
|
||||||
stream::iter(to_downloads)
|
stream::iter(to_downloads)
|
||||||
.map(|to_download| async move {
|
.map(|to_download| async move {
|
||||||
if let Err(e) = download_to_download(&to_download).await {
|
if let Err(e) = download_to_download(to_download).await {
|
||||||
println!("Error downloading {:?}: {}", &to_download.path, e);
|
println!("Error downloading {:?}: {}", &to_download.path, e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -68,11 +68,11 @@ pub async fn download_images_for_users<'b>(settings: &Settings, users: &Vec<Stea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn search_fo_to_download<'b>(
|
async fn search_fo_to_download(
|
||||||
known_images: Vec<String>,
|
known_images: Vec<String>,
|
||||||
user_data_folder: &str,
|
user_data_folder: &str,
|
||||||
shortcuts: &Vec<ShortcutOwned>,
|
shortcuts: &[ShortcutOwned],
|
||||||
search: &CachedSearch<'b>,
|
search: &CachedSearch<'_>,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
) -> Result<Vec<ToDownload>, Box<dyn Error>> {
|
) -> Result<Vec<ToDownload>, Box<dyn Error>> {
|
||||||
let shortcuts_to_search_for = shortcuts.iter().filter(|s| {
|
let shortcuts_to_search_for = shortcuts.iter().filter(|s| {
|
||||||
@@ -82,7 +82,7 @@ async fn search_fo_to_download<'b>(
|
|||||||
format!("{}_logo.png", s.app_id),
|
format!("{}_logo.png", s.app_id),
|
||||||
];
|
];
|
||||||
// if we are missing any of the images we need to search for them
|
// 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
|
images.iter().any(|image| !known_images.contains(image)) && !s.app_name.is_empty()
|
||||||
});
|
});
|
||||||
let shortcuts_to_search_for: Vec<&ShortcutOwned> = shortcuts_to_search_for.collect();
|
let shortcuts_to_search_for: Vec<&ShortcutOwned> = shortcuts_to_search_for.collect();
|
||||||
if shortcuts_to_search_for.is_empty() {
|
if shortcuts_to_search_for.is_empty() {
|
||||||
@@ -96,20 +96,16 @@ async fn search_fo_to_download<'b>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let search_result = search_result.unwrap();
|
let search_result = search_result.unwrap();
|
||||||
if search_result.is_none() {
|
search_result?;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let search_result = search_result.unwrap();
|
let search_result = search_result.unwrap();
|
||||||
Some((s.app_id, search_result))
|
Some((s.app_id, search_result))
|
||||||
})
|
})
|
||||||
.buffer_unordered(CONCURRENT_REQUESTS)
|
.buffer_unordered(CONCURRENT_REQUESTS)
|
||||||
.collect::<Vec<Option<(u32, usize)>>>()
|
.collect::<Vec<Option<(u32, usize)>>>()
|
||||||
.await;
|
.await;
|
||||||
for r in search_results_a {
|
for (app_id, search) in search_results_a.into_iter().flatten() {
|
||||||
if let Some((app_id, search)) = r {
|
|
||||||
search_results.insert(app_id, search);
|
search_results.insert(app_id, search);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
let types = vec![ImageType::Logo, ImageType::Hero, ImageType::Grid];
|
let types = vec![ImageType::Logo, ImageType::Hero, ImageType::Grid];
|
||||||
let mut to_download = vec![];
|
let mut to_download = vec![];
|
||||||
for image_type in types {
|
for image_type in types {
|
||||||
@@ -120,7 +116,7 @@ async fn search_fo_to_download<'b>(
|
|||||||
let image_ids: Vec<usize> = images_needed
|
let image_ids: Vec<usize> = images_needed
|
||||||
.clone()
|
.clone()
|
||||||
.filter_map(|s| search_results.get(&s.app_id))
|
.filter_map(|s| search_results.get(&s.app_id))
|
||||||
.map(|search| *search)
|
.copied()
|
||||||
.collect();
|
.collect();
|
||||||
use steamgriddb_api::query_parameters::QueryType::*;
|
use steamgriddb_api::query_parameters::QueryType::*;
|
||||||
let query_type = match image_type {
|
let query_type = match image_type {
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod symlinks;
|
mod symlinks;
|
||||||
mod sync;
|
mod synchronization;
|
||||||
|
|
||||||
pub use sync::run_sync;
|
pub use synchronization::run_sync;
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ use crate::{gog::GogPlatform, itch::ItchPlatform, origin::OriginPlatform};
|
|||||||
use std::{fs::File, io::Write, path::Path};
|
use std::{fs::File, io::Write, path::Path};
|
||||||
|
|
||||||
pub async fn run_sync(settings: &Settings) -> Result<(), Box<dyn Error>> {
|
pub async fn run_sync(settings: &Settings) -> Result<(), Box<dyn Error>> {
|
||||||
let userinfo_shortcuts = get_shortcuts_paths(&settings.steam)?;
|
let mut userinfo_shortcuts = get_shortcuts_paths(&settings.steam)?;
|
||||||
println!("Found {} user(s)", userinfo_shortcuts.len());
|
println!("Found {} user(s)", userinfo_shortcuts.len());
|
||||||
for user in userinfo_shortcuts.iter() {
|
for user in userinfo_shortcuts.iter_mut() {
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
let mut shortcut_info = get_shortcuts_for_user(user);
|
let mut shortcut_info = get_shortcuts_for_user(user);
|
||||||
@@ -29,6 +29,7 @@ pub async fn run_sync(settings: &Settings) -> Result<(), Box<dyn Error>> {
|
|||||||
update_platforms(settings, &mut shortcut_info.shortcuts);
|
update_platforms(settings, &mut shortcut_info.shortcuts);
|
||||||
fix_shortcut_icons(user, &mut shortcut_info.shortcuts);
|
fix_shortcut_icons(user, &mut shortcut_info.shortcuts);
|
||||||
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
save_shortcuts(&shortcut_info.shortcuts, Path::new(&shortcut_info.path));
|
||||||
|
user.shortcut_path = Some(shortcut_info.path.to_string_lossy().to_string());
|
||||||
|
|
||||||
let duration = start_time.elapsed();
|
let duration = start_time.elapsed();
|
||||||
println!("Finished synchronizing games in: {:?}", duration);
|
println!("Finished synchronizing games in: {:?}", duration);
|
||||||
@@ -91,7 +92,7 @@ fn update_platforms(settings: &Settings, new_user_shortcuts: &mut Vec<ShortcutOw
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save_shortcuts(shortcuts: &Vec<ShortcutOwned>, path: &Path) {
|
fn save_shortcuts(shortcuts: &[ShortcutOwned], path: &Path) {
|
||||||
let mut shortcuts_refs = vec![];
|
let mut shortcuts_refs = vec![];
|
||||||
for shortcut in shortcuts {
|
for shortcut in shortcuts {
|
||||||
shortcuts_refs.push(shortcut.borrow());
|
shortcuts_refs.push(shortcut.borrow());
|
||||||
@@ -99,7 +100,9 @@ fn save_shortcuts(shortcuts: &Vec<ShortcutOwned>, path: &Path) {
|
|||||||
let new_content = shortcuts_to_bytes(&shortcuts_refs);
|
let new_content = shortcuts_to_bytes(&shortcuts_refs);
|
||||||
match File::create(path) {
|
match File::create(path) {
|
||||||
Ok(mut file) => match file.write_all(new_content.as_slice()) {
|
Ok(mut file) => match file.write_all(new_content.as_slice()) {
|
||||||
Ok(_) => println!("Saved {} shortcuts", shortcuts.len()),
|
Ok(_) => {
|
||||||
|
println!("Saved {} shortcuts", shortcuts.len())
|
||||||
|
}
|
||||||
Err(e) => println!(
|
Err(e) => println!(
|
||||||
"Failed to save shortcuts to {} error: {}",
|
"Failed to save shortcuts to {} error: {}",
|
||||||
path.to_string_lossy(),
|
path.to_string_lossy(),
|
||||||
@@ -39,7 +39,6 @@ pub async fn run_ui() -> Result<(), Box<dyn Error>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui")]
|
|
||||||
fn empty_or_whitespace(input: String) -> Option<String> {
|
fn empty_or_whitespace(input: String) -> Option<String> {
|
||||||
if input.trim().is_empty() {
|
if input.trim().is_empty() {
|
||||||
None
|
None
|
||||||
@@ -47,7 +46,6 @@ fn empty_or_whitespace(input: String) -> Option<String> {
|
|||||||
Some(input)
|
Some(input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(feature = "ui")]
|
|
||||||
fn update_settings_with_ui_values(settings: &mut Settings, ui: &UserInterface) {
|
fn update_settings_with_ui_values(settings: &mut Settings, ui: &UserInterface) {
|
||||||
// Steam location
|
// Steam location
|
||||||
settings.steam.location = empty_or_whitespace(ui.steam_location_input.value());
|
settings.steam.location = empty_or_whitespace(ui.steam_location_input.value());
|
||||||
@@ -78,14 +76,12 @@ fn update_settings_with_ui_values(settings: &mut Settings, ui: &UserInterface) {
|
|||||||
settings.gog.wine_c_drive = empty_or_whitespace(ui.gog_winedrive_input.value());
|
settings.gog.wine_c_drive = empty_or_whitespace(ui.gog_winedrive_input.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui")]
|
|
||||||
|
|
||||||
fn save_settings_to_file(settings: &Settings) {
|
fn save_settings_to_file(settings: &Settings) {
|
||||||
let toml = toml::to_string(&settings).unwrap();
|
let toml = toml::to_string(&settings).unwrap();
|
||||||
std::fs::write("config.toml", toml).unwrap();
|
std::fs::write("config.toml", toml).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "ui")]
|
|
||||||
fn update_ui_with_settings(ui: &mut UserInterface, settings: &Settings) {
|
fn update_ui_with_settings(ui: &mut UserInterface, settings: &Settings) {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user