Backup shortcuts (#144)

* Add initial option to back up shortcuts

* Show list of backups

* Reload backups on change

* Restore backups

* Improve UX of backup ui

* Reorganize ui

* Update version in cargo.toml

* Update flatpak description

* Update cargo-lock.json
This commit is contained in:
Philip Kristoffersen
2022-05-20 21:53:21 +02:00
committed by GitHub
parent f5fef331ab
commit 967465d326
9 changed files with 269 additions and 37 deletions
Generated
+25 -1
View File
@@ -148,9 +148,10 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]] [[package]]
name = "boilr" name = "boilr"
version = "1.3.6" version = "1.3.7"
dependencies = [ dependencies = [
"base64", "base64",
"chrono",
"config", "config",
"copypasta", "copypasta",
"dashmap", "dashmap",
@@ -269,6 +270,19 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits 0.2.15",
"time",
"winapi",
]
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "3.1.1" version = "3.1.1"
@@ -2542,6 +2556,16 @@ dependencies = [
"weezl", "weezl",
] ]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
+53 -27
View File
@@ -1,35 +1,61 @@
[package] [package]
name = "boilr"
version = "1.3.6"
edition = "2021" edition = "2021"
name = "boilr"
version = "1.3.7"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
steam_shortcuts_util = "1.1.7"
steamgriddb_api = "^0.3.0"
serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.81"
tokio = { version = "^1.18.2", features = ["full"] }
reqwest = { version = "^0.11.10", default_features = false }
config = "^0.11.0"
failure = "^0.1.8"
nom = "^7.1.1"
flate2 = "^1.0.23"
futures = { version = "^0.3.21" }
dashmap = { version = "^5.3.3", features = ["serde"] }
is_executable = "^1.0.1"
rusty-leveldb = "^0.3.6"
base64 = "^0.13.0" base64 = "^0.13.0"
eframe = { version = "^0.18.0" } chrono = "^0.4.19"
egui = { version = "^0.18.1" } config = "^0.11.0"
image = { version = "0.24.2", features = ["png"] }
toml = { version = "^0.5.9" }
sysinfo = "^0.23.12"
sqlite = "0.26.0"
copypasta = "0.7.1" copypasta = "0.7.1"
failure = "^0.1.8"
flate2 = "^1.0.23"
is_executable = "^1.0.1"
nom = "^7.1.1"
rusty-leveldb = "^0.3.6"
serde_json = "^1.0.81"
sqlite = "^0.26.0"
steam_shortcuts_util = "^1.1.7"
steamgriddb_api = "^0.3.0"
sysinfo = "^0.23.12"
[target.'cfg(windows)'.dependencies] [dependencies.dashmap]
winreg = "0.10.1" features = ["serde"]
version = "^5.3.3"
[dependencies.eframe]
version = "^0.18.0"
[dependencies.egui]
version = "^0.18.1"
[dependencies.futures]
version = "^0.3.21"
[dependencies.image]
features = ["png"]
version = "^0.24.2"
[dependencies.reqwest]
default_features = false
version = "^0.11.10"
[dependencies.serde]
features = ["derive"]
version = "^1.0.137"
[dependencies.tokio]
features = ["full"]
version = "^1.18.2"
[dependencies.toml]
version = "^0.5.9"
[target]
[target."cfg(windows)"]
[target."cfg(windows)".build-dependencies]
winres = "^0.1"
[target."cfg(windows)".dependencies]
winreg = "^0.10.1"
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
+26
View File
@@ -402,6 +402,19 @@
"dest": "cargo/vendor/cgl-0.3.2", "dest": "cargo/vendor/cgl-0.3.2",
"dest-filename": ".cargo-checksum.json" "dest-filename": ".cargo-checksum.json"
}, },
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/chrono/chrono-0.4.19.crate",
"sha256": "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73",
"dest": "cargo/vendor/chrono-0.4.19"
},
{
"type": "inline",
"contents": "{\"package\": \"670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73\", \"files\": {}}",
"dest": "cargo/vendor/chrono-0.4.19",
"dest-filename": ".cargo-checksum.json"
},
{ {
"type": "archive", "type": "archive",
"archive-type": "tar-gzip", "archive-type": "tar-gzip",
@@ -3327,6 +3340,19 @@
"dest": "cargo/vendor/tiff-0.7.2", "dest": "cargo/vendor/tiff-0.7.2",
"dest-filename": ".cargo-checksum.json" "dest-filename": ".cargo-checksum.json"
}, },
{
"type": "archive",
"archive-type": "tar-gzip",
"url": "https://static.crates.io/crates/time/time-0.1.43.crate",
"sha256": "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438",
"dest": "cargo/vendor/time-0.1.43"
},
{
"type": "inline",
"contents": "{\"package\": \"ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438\", \"files\": {}}",
"dest": "cargo/vendor/time-0.1.43",
"dest-filename": ".cargo-checksum.json"
},
{ {
"type": "archive", "type": "archive",
"archive-type": "tar-gzip", "archive-type": "tar-gzip",
+7 -2
View File
@@ -10,7 +10,7 @@
<p>This little tool will show games from other games platforms in your Steam library. <p>This little tool will show games from other games platforms in your Steam library.
It uses the Steam 3rd party shortcuts feature and does not require you to set up anything. It uses the Steam 3rd party shortcuts feature and does not require you to set up anything.
The goal is that you do not have to leave your Steam library to launch games from other launchers/stores, so that you can find all the games that you have available. The goal is that you do not have to leave your Steam library to launch games from other launchers/stores, so that you can find all the games that you have available.
Optionally you can set BoilR up to automatically download artwork from SteamGridDB.</p> You can also use BoilR to manually and automatically download custom art from SteamGridDB.</p>
</description> </description>
<screenshots> <screenshots>
<screenshot type="default"> <screenshot type="default">
@@ -25,7 +25,12 @@ https://hughsie.github.io/oars/index.html
--> -->
<content_rating type="oars-1.1" /> <content_rating type="oars-1.1" />
<releases> <releases>
<release version="1.3.5" date="2022-05-17"> <release version="1.3.7" date="2022-05-17">
<description>
<p>Back up your shortcuts and restore them later</p>
</description>
</release>
<release version="1.3.6" date="2022-05-17">
<description> <description>
<p>Automatically find Steam installed through flatpak</p> <p>Automatically find Steam installed through flatpak</p>
</description> </description>
+6
View File
@@ -37,6 +37,12 @@ pub fn get_cache_file() -> PathBuf {
get_config_folder().join("cache.json").to_path_buf() get_config_folder().join("cache.json").to_path_buf()
} }
pub fn get_backups_flder() -> PathBuf {
let backups_path = get_config_folder().join("backup");
let _ = create_dir_all(&backups_path);
backups_path.to_path_buf()
}
#[cfg(target_family = "unix")] #[cfg(target_family = "unix")]
pub fn get_boilr_links_path() -> PathBuf { pub fn get_boilr_links_path() -> PathBuf {
get_config_folder().join("links").to_path_buf() get_config_folder().join("links").to_path_buf()
+2
View File
@@ -1,10 +1,12 @@
mod defines; mod defines;
mod ui_backup;
mod ui_image_download; mod ui_image_download;
mod ui_import_games; mod ui_import_games;
mod ui_settings; mod ui_settings;
mod uiapp; mod uiapp;
pub use defines::*; pub use defines::*;
pub use ui_backup::*;
pub use ui_image_download::*; pub use ui_image_download::*;
pub use ui_import_games::*; pub use ui_import_games::*;
pub use ui_settings::*; pub use ui_settings::*;
+124
View File
@@ -0,0 +1,124 @@
use std::path::{Path, PathBuf};
use chrono::Local;
use egui::ScrollArea;
use crate::{
config::get_backups_flder,
steam::{get_shortcuts_paths, SteamSettings},
};
use super::MyEguiApp;
#[derive(Default)]
pub struct BackupState {
pub available_backups: Option<Vec<PathBuf>>,
pub last_restore: Option<PathBuf>,
}
impl MyEguiApp {
pub fn render_backup(&mut self, ui: &mut egui::Ui) {
ui.heading("Backups");
ui.label("Here you can restore backed up shortcuts files");
ui.label("Click a backup to restore it, your current shortcuts will be backed up first");
ui.add_space(15.0);
if let Some(last_restore) = self.backup_state.last_restore.as_ref() {
ui.heading(format!("Last restored {:?}", last_restore));
}
if ui.button("Click here to create a new backup").clicked() {
backup_shortcuts(&self.settings.steam);
self.backup_state.available_backups = None;
}
let available_backups = self
.backup_state
.available_backups
.get_or_insert_with(|| load_backups());
if available_backups.is_empty() {
ui.label("No backups found, they will be created every time you run import");
} else {
ScrollArea::vertical()
.stick_to_right()
.auto_shrink([false, true])
.show(ui, |ui| {
for backup_path in available_backups {
if ui
.button(backup_path.to_string_lossy().to_string())
.clicked()
{
//Restore
backup_shortcuts(&self.settings.steam);
if restore_backup(&self.settings.steam, backup_path.as_path()) {
self.backup_state.last_restore = Some(backup_path.clone());
}
}
}
});
}
}
}
pub fn restore_backup(steam_settings: &SteamSettings, shortcut_path: &Path) -> bool {
let file_name = shortcut_path.file_name().unwrap();
let paths = get_shortcuts_paths(steam_settings);
if let Ok(paths) = paths {
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);
return true;
}
}
}
}
return false;
}
pub fn load_backups() -> Vec<PathBuf> {
let backup_folder = get_backups_flder();
let files = std::fs::read_dir(&backup_folder);
let mut result = vec![];
if let Ok(files) = files {
for file in files {
if let Ok(file) = file {
if file
.path()
.extension()
.unwrap_or_default()
.to_string_lossy()
== "vdf"
{
result.push(file.path().to_path_buf());
}
}
}
}
result.sort();
result.reverse();
return result;
}
pub fn backup_shortcuts(steam_settings: &SteamSettings) {
let backup_folder = get_backups_flder();
let paths = get_shortcuts_paths(&steam_settings);
let date = Local::now();
let date_string = date.format("%Y-%m-%d-%H-%M-%S");
if let Ok(user_infos) = 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();
}
}
}
}
+5 -1
View File
@@ -9,7 +9,7 @@ use crate::sync;
use crate::sync::{download_images, SyncProgress}; use crate::sync::{download_images, SyncProgress};
use super::ImageSelectState; use super::{ImageSelectState, backup_shortcuts};
use super::{ use super::{
ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR}, ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR},
MyEguiApp, MyEguiApp,
@@ -42,6 +42,9 @@ impl<T> FetcStatus<T> {
} }
impl MyEguiApp { impl MyEguiApp {
pub(crate) fn render_import_games(&mut self, ui: &mut egui::Ui) { pub(crate) fn render_import_games(&mut self, ui: &mut egui::Ui) {
ui.heading("Import Games"); ui.heading("Import Games");
@@ -118,6 +121,7 @@ impl MyEguiApp {
self.rt.spawn_blocking(move || { self.rt.spawn_blocking(move || {
MyEguiApp::save_settings_to_file(&settings); MyEguiApp::save_settings_to_file(&settings);
let mut some_sender = Some(sender); let mut some_sender = Some(sender);
backup_shortcuts(&settings.steam);
let usersinfo = sync::run_sync(&settings, &mut some_sender).unwrap(); let usersinfo = sync::run_sync(&settings, &mut some_sender).unwrap();
let task = download_images(&settings, &usersinfo, &mut some_sender); let task = download_images(&settings, &usersinfo, &mut some_sender);
block_on(task); block_on(task);
+21 -6
View File
@@ -17,7 +17,7 @@ use super::{
}, },
ui_images::{get_import_image, get_logo, get_logo_icon}, ui_images::{get_import_image, get_logo, get_logo_icon},
ui_import_games::FetcStatus, ui_import_games::FetcStatus,
ImageSelectState, BackupState, ImageSelectState,
}; };
const SECTION_SPACING: f32 = 25.0; const SECTION_SPACING: f32 = 25.0;
@@ -37,6 +37,7 @@ pub struct MyEguiApp {
pub(crate) status_reciever: Receiver<SyncProgress>, pub(crate) status_reciever: Receiver<SyncProgress>,
pub(crate) epic_manifests: Option<Vec<ManifestItem>>, pub(crate) epic_manifests: Option<Vec<ManifestItem>>,
pub(crate) image_selected_state: ImageSelectState, pub(crate) image_selected_state: ImageSelectState,
pub(crate) backup_state: BackupState,
} }
impl MyEguiApp { impl MyEguiApp {
@@ -51,6 +52,7 @@ impl MyEguiApp {
status_reciever: watch::channel(SyncProgress::NotStarted).1, status_reciever: watch::channel(SyncProgress::NotStarted).1,
epic_manifests: None, epic_manifests: None,
image_selected_state: ImageSelectState::default(), image_selected_state: ImageSelectState::default(),
backup_state: BackupState::default(),
} }
} }
} }
@@ -60,6 +62,7 @@ enum Menues {
Import, Import,
Settings, Settings,
Images, Images,
Backup,
} }
impl Default for Menues { impl Default for Menues {
@@ -82,19 +85,28 @@ impl App for MyEguiApp {
ui.image(texture, size); ui.image(texture, size);
ui.add_space(SECTION_SPACING); ui.add_space(SECTION_SPACING);
let changed = ui let mut changed = ui
.selectable_value(&mut self.selected_menu, Menues::Import, "Import Games") .selectable_value(&mut self.selected_menu, Menues::Import, "Import Games")
.changed(); .changed();
let mut changed = changed
|| ui
.selectable_value(&mut self.selected_menu, Menues::Settings, "Settings")
.changed();
if self.settings.steamgrid_db.auth_key.is_some() { if self.settings.steamgrid_db.auth_key.is_some() {
changed = changed changed = changed
|| ui || ui
.selectable_value(&mut self.selected_menu, Menues::Images, "Images") .selectable_value(&mut self.selected_menu, Menues::Images, "Images")
.changed(); .changed();
} }
changed = changed
|| ui
.selectable_value(&mut self.selected_menu, Menues::Settings, "Settings")
.changed();
changed = changed
|| ui
.selectable_value(&mut self.selected_menu, Menues::Backup, "Backup")
.changed();
if changed {
self.backup_state.available_backups = None;
}
if changed && self.selected_menu == Menues::Settings { if changed && self.selected_menu == Menues::Settings {
//We reset games here, since user might change settings //We reset games here, since user might change settings
self.games_to_sync = watch::channel(FetcStatus::NeedsFetched).1; self.games_to_sync = watch::channel(FetcStatus::NeedsFetched).1;
@@ -155,6 +167,9 @@ impl App for MyEguiApp {
Menues::Images => { Menues::Images => {
self.render_ui_images(ui); self.render_ui_images(ui);
} }
Menues::Backup => {
self.render_backup(ui);
}
}; };
}); });
} }