From 4cc5041f0a04dc1066bc00917ce003dd3569b49e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Mon, 3 Feb 2025 17:43:44 +0100 Subject: [PATCH] Improve cleanup. --- Penumbra/Services/CleanupService.cs | 175 +++++++++++++++++++++------- Penumbra/UI/Tabs/SettingsTab.cs | 28 +++-- 2 files changed, 154 insertions(+), 49 deletions(-) diff --git a/Penumbra/Services/CleanupService.cs b/Penumbra/Services/CleanupService.cs index 490c2407..bf76f5f0 100644 --- a/Penumbra/Services/CleanupService.cs +++ b/Penumbra/Services/CleanupService.cs @@ -6,69 +6,160 @@ namespace Penumbra.Services; public class CleanupService(SaveService saveService, ModManager mods, CollectionManager collections) : IService { + private CancellationTokenSource _cancel = new(); + private Task? _task; + + public double Progress { get; private set; } + + public bool IsRunning + => _task is { IsCompleted: false }; + + public void Cancel() + => _cancel.Cancel(); + public void CleanUnusedLocalData() { - var usedFiles = mods.Select(saveService.FileNames.LocalDataFile).ToHashSet(); - foreach (var file in saveService.FileNames.LocalDataFiles.ToList()) - { - try - { - if (!file.Exists || usedFiles.Contains(file.FullName)) - continue; + if (IsRunning) + return; - file.Delete(); - Penumbra.Log.Information($"[CleanupService] Deleted unused local data file {file.Name}."); - } - catch (Exception ex) + var usedFiles = mods.Select(saveService.FileNames.LocalDataFile).ToHashSet(); + Progress = 0; + var deleted = 0; + _cancel = new CancellationTokenSource(); + _task = Task.Run(() => + { + var localFiles = saveService.FileNames.LocalDataFiles.ToList(); + var step = 0.9 / localFiles.Count; + Progress = 0.1; + foreach (var file in localFiles) { - Penumbra.Log.Error($"[CleanupService] Failed to delete unused local data file {file.Name}:\n{ex}"); + if (_cancel.IsCancellationRequested) + break; + + try + { + if (!file.Exists || usedFiles.Contains(file.FullName)) + continue; + + file.Delete(); + Penumbra.Log.Debug($"[CleanupService] Deleted unused local data file {file.Name}."); + ++deleted; + } + catch (Exception ex) + { + Penumbra.Log.Error($"[CleanupService] Failed to delete unused local data file {file.Name}:\n{ex}"); + } + + Progress += step; } - } + + Penumbra.Log.Information($"[CleanupService] Deleted {deleted} unused local data files."); + Progress = 1; + }); } public void CleanBackupFiles() { - foreach (var file in mods.BasePath.EnumerateFiles("group_*.json.bak", SearchOption.AllDirectories)) + if (IsRunning) + return; + + Progress = 0; + var deleted = 0; + _cancel = new CancellationTokenSource(); + _task = Task.Run(() => { - try - { - if (!file.Exists) - continue; + var configFiles = Directory.EnumerateFiles(saveService.FileNames.ConfigDirectory, "*.json.bak", SearchOption.AllDirectories) + .ToList(); + Progress = 0.1; + if (_cancel.IsCancellationRequested) + return; - file.Delete(); - Penumbra.Log.Information($"[CleanupService] Deleted group backup file {file.FullName}."); - } - catch (Exception ex) + var groupFiles = mods.BasePath.EnumerateFiles("group_*.json.bak", SearchOption.AllDirectories).ToList(); + Progress = 0.5; + var step = 0.4 / (groupFiles.Count + configFiles.Count); + foreach (var file in groupFiles) { - Penumbra.Log.Error($"[CleanupService] Failed to delete group backup file {file.FullName}:\n{ex}"); - } - } + if (_cancel.IsCancellationRequested) + break; - foreach (var file in Directory.EnumerateFiles(saveService.FileNames.ConfigDirectory, "*.json.bak", SearchOption.AllDirectories)) - { - try - { - if (!File.Exists(file)) - continue; + try + { + if (!file.Exists) + continue; - File.Delete(file); - Penumbra.Log.Information($"[CleanupService] Deleted config backup file {file}."); + file.Delete(); + ++deleted; + Penumbra.Log.Debug($"[CleanupService] Deleted group backup file {file.FullName}."); + } + catch (Exception ex) + { + Penumbra.Log.Error($"[CleanupService] Failed to delete group backup file {file.FullName}:\n{ex}"); + } + + Progress += step; } - catch (Exception ex) + + Penumbra.Log.Information($"[CleanupService] Deleted {deleted} group backup files."); + + deleted = 0; + foreach (var file in configFiles) { - Penumbra.Log.Error($"[CleanupService] Failed to delete config backup file {file}:\n{ex}"); + if (_cancel.IsCancellationRequested) + break; + + try + { + if (!File.Exists(file)) + continue; + + File.Delete(file); + ++deleted; + Penumbra.Log.Debug($"[CleanupService] Deleted config backup file {file}."); + } + catch (Exception ex) + { + Penumbra.Log.Error($"[CleanupService] Failed to delete config backup file {file}:\n{ex}"); + } + + Progress += step; } - } + + Penumbra.Log.Information($"[CleanupService] Deleted {deleted} config backup files."); + Progress = 1; + }); } public void CleanupAllUnusedSettings() { - foreach (var collection in collections.Storage) + if (IsRunning) + return; + + Progress = 0; + var totalRemoved = 0; + var diffCollections = 0; + _cancel = new CancellationTokenSource(); + _task = Task.Run(() => { - var count = collections.Storage.CleanUnavailableSettings(collection); - if (count > 0) - Penumbra.Log.Information( - $"[CleanupService] Removed {count} unused settings from collection {collection.Identity.AnonymizedName}."); - } + var step = 1.0 / collections.Storage.Count; + foreach (var collection in collections.Storage) + { + if (_cancel.IsCancellationRequested) + break; + + var count = collections.Storage.CleanUnavailableSettings(collection); + if (count > 0) + { + Penumbra.Log.Debug( + $"[CleanupService] Removed {count} unused settings from collection {collection.Identity.AnonymizedName}."); + totalRemoved += count; + ++diffCollections; + } + + Progress += step; + } + + Penumbra.Log.Information($"[CleanupService] Removed {totalRemoved} unused settings from {diffCollections} separate collections."); + Progress = 1; + }); } } diff --git a/Penumbra/UI/Tabs/SettingsTab.cs b/Penumbra/UI/Tabs/SettingsTab.cs index e847b291..9637adeb 100644 --- a/Penumbra/UI/Tabs/SettingsTab.cs +++ b/Penumbra/UI/Tabs/SettingsTab.cs @@ -797,7 +797,6 @@ public class SettingsTab : ITab, IUiService ImGui.Separator(); DrawCleanupButtons(); ImGui.NewLine(); - } private void DrawCrashHandler() @@ -991,24 +990,39 @@ public class SettingsTab : ITab, IUiService private void DrawCleanupButtons() { var enabled = _config.DeleteModModifier.IsActive(); + if (_cleanupService.Progress is not 0.0 and not 1.0) + { + ImUtf8.ProgressBar((float)_cleanupService.Progress, new Vector2(200 * ImUtf8.GlobalScale, ImGui.GetFrameHeight()), + $"{_cleanupService.Progress * 100}%"); + ImGui.SameLine(); + if (ImUtf8.Button("Cancel##FileCleanup"u8)) + _cleanupService.Cancel(); + } + else + { + ImGui.NewLine(); + } + if (ImUtf8.ButtonEx("Clear Unused Local Mod Data Files"u8, - "Delete all local mod data files that do not correspond to currently installed mods."u8, default, !enabled)) + "Delete all local mod data files that do not correspond to currently installed mods."u8, default, + !enabled || _cleanupService.IsRunning)) _cleanupService.CleanUnusedLocalData(); if (!enabled) - ImUtf8.HoverTooltip($"Hold {_config.DeleteModModifier} while clicking to delete files."); + ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking to delete files."); if (ImUtf8.ButtonEx("Clear Backup Files"u8, "Delete all backups of .json configuration files in your configuration folder and all backups of mod group files in your mod directory."u8, - default, !enabled)) + default, !enabled || _cleanupService.IsRunning)) _cleanupService.CleanBackupFiles(); if (!enabled) - ImUtf8.HoverTooltip($"Hold {_config.DeleteModModifier} while clicking to delete files."); + ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking to delete files."); if (ImUtf8.ButtonEx("Clear All Unused Settings"u8, - "Remove all mod settings in all of your collections that do not correspond to currently installed mods."u8, default, !enabled)) + "Remove all mod settings in all of your collections that do not correspond to currently installed mods."u8, default, + !enabled || _cleanupService.IsRunning)) _cleanupService.CleanupAllUnusedSettings(); if (!enabled) - ImUtf8.HoverTooltip($"Hold {_config.DeleteModModifier} while clicking to remove settings."); + ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, $"Hold {_config.DeleteModModifier} while clicking to remove settings."); } /// Draw a checkbox that toggles the dalamud setting to wait for plugins on open.