diff --git a/CHANGELOG.md b/CHANGELOG.md index baaa846..e1abdaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ [Ludusavi v0.7.0 or newer](https://github.com/mtkennerly/ludusavi/releases) is now required. * Added: + * After doing a backup or restore of all games, you can click the notification + for a full list of all games processed. * "Browse" button for Ludusavi executable file. * "Browse" and "open" buttons for backup directory. * Fixed: @@ -17,6 +19,7 @@ * File size units are now adjusted based on the size, rather than always using MiB. * The default backup directory now writes out the user folder in full rather than using the `~` placeholder (although that is still supported). + * You can now dismiss notifications by clicking on them. ## v0.1.0 (2020-07-29) diff --git a/src/LudusaviPlaynite.cs b/src/LudusaviPlaynite.cs index 61b3d3a..9340c24 100644 --- a/src/LudusaviPlaynite.cs +++ b/src/LudusaviPlaynite.cs @@ -1,10 +1,12 @@ -using Newtonsoft.Json; +using ByteSizeLib; +using Newtonsoft.Json; using Playnite.SDK; using Playnite.SDK.Models; using Playnite.SDK.Plugins; using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -125,12 +127,47 @@ public override UserControl GetSettingsView(bool firstRunSettings) private void NotifyInfo(string message) { - PlayniteApi.Notifications.Add(Guid.NewGuid().ToString(), message, NotificationType.Info); + NotifyInfo(message, () => { }); + } + + private void NotifyInfo(string message, Action action) + { + PlayniteApi.Notifications.Add(new NotificationMessage(Guid.NewGuid().ToString(), message, NotificationType.Info, action)); } private void NotifyError(string message) { - PlayniteApi.Notifications.Add(Guid.NewGuid().ToString(), message, NotificationType.Error); + NotifyError(message, () => { }); + } + + private void NotifyError(string message, Action action) + { + PlayniteApi.Notifications.Add(new NotificationMessage(Guid.NewGuid().ToString(), message, NotificationType.Error, action)); + } + + private void ShowFullResults(ApiResponse response) + { + var tempFile = Path.GetTempPath() + Guid.NewGuid().ToString() + ".html"; + using (StreamWriter sw = File.CreateText(tempFile)) + { + sw.WriteLine(""); + } + + var webview = PlayniteApi.WebViews.CreateView(640, 480); + webview.Navigate(tempFile); + webview.OpenDialog(); + + try + { + File.Delete(tempFile); + } + catch + { } } private (int, string) RunCommand(string command, string args) @@ -302,11 +339,11 @@ private void BackUpAllGames() if (code == 0) { - NotifyInfo(translator.BackUpAllGames_Success(result)); + NotifyInfo(translator.BackUpAllGames_Success(result), () => ShowFullResults(result.Response)); } else { - NotifyError(translator.BackUpAllGames_Failure(result)); + NotifyError(translator.BackUpAllGames_Failure(result), () => ShowFullResults(result.Response)); } } pendingOperation = false; @@ -369,11 +406,11 @@ private void RestoreAllGames() if (code == 0) { - NotifyInfo(translator.RestoreAllGames_Success(result)); + NotifyInfo(translator.RestoreAllGames_Success(result), () => ShowFullResults(result.Response)); } else { - NotifyError(translator.RestoreAllGames_Failure(result)); + NotifyError(translator.RestoreAllGames_Failure(result), () => ShowFullResults(result.Response)); } } diff --git a/src/Models.cs b/src/Models.cs index e38e9ae..23a5dbb 100644 --- a/src/Models.cs +++ b/src/Models.cs @@ -36,11 +36,39 @@ public struct ApiOverall public int ProcessedBytes; } + public struct ApiFile + { + [JsonProperty("failed")] + public bool Failed; + [JsonProperty("bytes")] + public int Bytes; + [JsonProperty("originalPath")] + public string OriginalPath; + } + + public struct ApiRegistry + { + [JsonProperty("failed")] + public bool Failed; + } + + public struct ApiGame + { + [JsonProperty("decision")] + public string Decision; + [JsonProperty("files")] + public Dictionary Files; + [JsonProperty("registry")] + public Dictionary Registry; + } + public struct ApiResponse { [JsonProperty("errors")] public ApiErrors Errors; [JsonProperty("overall")] public ApiOverall Overall; + [JsonProperty("games")] + public Dictionary Games; } } diff --git a/src/Translator.cs b/src/Translator.cs index c4c1c1d..a3dc35f 100644 --- a/src/Translator.cs +++ b/src/Translator.cs @@ -1,5 +1,6 @@ using ByteSizeLib; using System; +using System.Linq; namespace LudusaviPlaynite { @@ -203,7 +204,7 @@ public string BackUpAllGames_Success(OperationResult result) { default: return string.Format( - "Backed up saves for {0} games ({1})", + "Backed up saves for {0} games ({1}); click for full list", result.Response.Overall.ProcessedGames, AdjustedSize(result.Response.Overall.ProcessedBytes) ); @@ -216,7 +217,7 @@ public string BackUpAllGames_Failure(OperationResult result) { default: return string.Format( - "Backed up saves for {0} of {1} games ({2} of {3}), but some failed", + "Backed up saves for {0} of {1} games ({2} of {3}), but some failed; click for full list", result.Response.Overall.ProcessedGames, result.Response.Overall.TotalGames, AdjustedSize(result.Response.Overall.ProcessedBytes), @@ -270,7 +271,7 @@ public string RestoreAllGames_Success(OperationResult result) { default: return string.Format( - "Restored saves for {0} games ({1})", + "Restored saves for {0} games ({1}); click for full list", result.Response.Overall.ProcessedGames, AdjustedSize(result.Response.Overall.ProcessedBytes) ); @@ -283,7 +284,7 @@ public string RestoreAllGames_Failure(OperationResult result) { default: return string.Format( - "Restored saves for {0} of {1} games ({2} of {3}), but some failed", + "Restored saves for {0} of {1} games ({2} of {3}), but some failed; click for full list", result.Response.Overall.ProcessedGames, result.Response.Overall.TotalGames, AdjustedSize(result.Response.Overall.ProcessedBytes), @@ -292,6 +293,47 @@ public string RestoreAllGames_Failure(OperationResult result) } } + public string FullListGameLineItem_Failed() + { + switch (language) + { + default: + return "FAILED"; + } + } + + public string FullListGameLineItem_Ignored() + { + switch (language) + { + default: + return "IGNORED"; + } + } + + public string FullListGameLineItem(string name, ApiGame game) + { + var size = AdjustedSize(game.Files.Sum(x => x.Value.Bytes)); + var failed = game.Files.Any(x => x.Value.Failed) || game.Registry.Any(x => x.Value.Failed); + + switch (language) + { + default: + if (failed) + { + return string.Format("[{0}] {1} ({2})", FullListGameLineItem_Failed(), name, size); + } + else if (game.Decision == "Ignored") + { + return string.Format("[{0}] {1} ({2})", FullListGameLineItem_Ignored(), name, size); + } + else + { + return string.Format("{0} ({1})", name, size); + } + } + } + public string ExecutablePath_Label() { switch (language)