mirror of
https://github.com/djibux/BoilR.git
synced 2026-09-01 05:53:41 +02:00
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:
Generated
+25
-1
@@ -148,9 +148,10 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "boilr"
|
||||
version = "1.3.6"
|
||||
version = "1.3.7"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"chrono",
|
||||
"config",
|
||||
"copypasta",
|
||||
"dashmap",
|
||||
@@ -269,6 +270,19 @@ dependencies = [
|
||||
"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]]
|
||||
name = "clipboard-win"
|
||||
version = "3.1.1"
|
||||
@@ -2542,6 +2556,16 @@ dependencies = [
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
||||
+53
-27
@@ -1,35 +1,61 @@
|
||||
[package]
|
||||
name = "boilr"
|
||||
version = "1.3.6"
|
||||
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]
|
||||
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"
|
||||
eframe = { version = "^0.18.0" }
|
||||
egui = { version = "^0.18.1" }
|
||||
image = { version = "0.24.2", features = ["png"] }
|
||||
toml = { version = "^0.5.9" }
|
||||
sysinfo = "^0.23.12"
|
||||
sqlite = "0.26.0"
|
||||
chrono = "^0.4.19"
|
||||
config = "^0.11.0"
|
||||
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]
|
||||
winreg = "0.10.1"
|
||||
[dependencies.dashmap]
|
||||
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"
|
||||
|
||||
@@ -402,6 +402,19 @@
|
||||
"dest": "cargo/vendor/cgl-0.3.2",
|
||||
"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",
|
||||
"archive-type": "tar-gzip",
|
||||
@@ -3327,6 +3340,19 @@
|
||||
"dest": "cargo/vendor/tiff-0.7.2",
|
||||
"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",
|
||||
"archive-type": "tar-gzip",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<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.
|
||||
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>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
@@ -25,7 +25,12 @@ https://hughsie.github.io/oars/index.html
|
||||
-->
|
||||
<content_rating type="oars-1.1" />
|
||||
<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>
|
||||
<p>Automatically find Steam installed through flatpak</p>
|
||||
</description>
|
||||
|
||||
@@ -37,6 +37,12 @@ pub fn get_cache_file() -> PathBuf {
|
||||
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")]
|
||||
pub fn get_boilr_links_path() -> PathBuf {
|
||||
get_config_folder().join("links").to_path_buf()
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
mod defines;
|
||||
mod ui_backup;
|
||||
mod ui_image_download;
|
||||
mod ui_import_games;
|
||||
mod ui_settings;
|
||||
mod uiapp;
|
||||
|
||||
pub use defines::*;
|
||||
pub use ui_backup::*;
|
||||
pub use ui_image_download::*;
|
||||
pub use ui_import_games::*;
|
||||
pub use ui_settings::*;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use crate::sync;
|
||||
|
||||
use crate::sync::{download_images, SyncProgress};
|
||||
|
||||
use super::ImageSelectState;
|
||||
use super::{ImageSelectState, backup_shortcuts};
|
||||
use super::{
|
||||
ui_colors::{BACKGROUND_COLOR, EXTRA_BACKGROUND_COLOR},
|
||||
MyEguiApp,
|
||||
@@ -42,6 +42,9 @@ impl<T> FetcStatus<T> {
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
|
||||
|
||||
|
||||
pub(crate) fn render_import_games(&mut self, ui: &mut egui::Ui) {
|
||||
ui.heading("Import Games");
|
||||
|
||||
@@ -118,6 +121,7 @@ impl MyEguiApp {
|
||||
self.rt.spawn_blocking(move || {
|
||||
MyEguiApp::save_settings_to_file(&settings);
|
||||
let mut some_sender = Some(sender);
|
||||
backup_shortcuts(&settings.steam);
|
||||
let usersinfo = sync::run_sync(&settings, &mut some_sender).unwrap();
|
||||
let task = download_images(&settings, &usersinfo, &mut some_sender);
|
||||
block_on(task);
|
||||
|
||||
+21
-6
@@ -17,7 +17,7 @@ use super::{
|
||||
},
|
||||
ui_images::{get_import_image, get_logo, get_logo_icon},
|
||||
ui_import_games::FetcStatus,
|
||||
ImageSelectState,
|
||||
BackupState, ImageSelectState,
|
||||
};
|
||||
|
||||
const SECTION_SPACING: f32 = 25.0;
|
||||
@@ -37,6 +37,7 @@ pub struct MyEguiApp {
|
||||
pub(crate) status_reciever: Receiver<SyncProgress>,
|
||||
pub(crate) epic_manifests: Option<Vec<ManifestItem>>,
|
||||
pub(crate) image_selected_state: ImageSelectState,
|
||||
pub(crate) backup_state: BackupState,
|
||||
}
|
||||
|
||||
impl MyEguiApp {
|
||||
@@ -51,6 +52,7 @@ impl MyEguiApp {
|
||||
status_reciever: watch::channel(SyncProgress::NotStarted).1,
|
||||
epic_manifests: None,
|
||||
image_selected_state: ImageSelectState::default(),
|
||||
backup_state: BackupState::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,6 +62,7 @@ enum Menues {
|
||||
Import,
|
||||
Settings,
|
||||
Images,
|
||||
Backup,
|
||||
}
|
||||
|
||||
impl Default for Menues {
|
||||
@@ -82,19 +85,28 @@ impl App for MyEguiApp {
|
||||
ui.image(texture, size);
|
||||
ui.add_space(SECTION_SPACING);
|
||||
|
||||
let changed = ui
|
||||
let mut changed = ui
|
||||
.selectable_value(&mut self.selected_menu, Menues::Import, "Import Games")
|
||||
.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() {
|
||||
changed = changed
|
||||
|| ui
|
||||
.selectable_value(&mut self.selected_menu, Menues::Images, "Images")
|
||||
.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 {
|
||||
//We reset games here, since user might change settings
|
||||
self.games_to_sync = watch::channel(FetcStatus::NeedsFetched).1;
|
||||
@@ -155,6 +167,9 @@ impl App for MyEguiApp {
|
||||
Menues::Images => {
|
||||
self.render_ui_images(ui);
|
||||
}
|
||||
Menues::Backup => {
|
||||
self.render_backup(ui);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user