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("
");
+ foreach (var game in response.Games)
+ {
+ sw.WriteLine(string.Format("- {0}
", translator.FullListGameLineItem(game.Key, game.Value)));
+ }
+ 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)