diff --git a/StabilityMatrix/Helper/ISettingsManager.cs b/StabilityMatrix/Helper/ISettingsManager.cs index b9c72db1..018775dd 100644 --- a/StabilityMatrix/Helper/ISettingsManager.cs +++ b/StabilityMatrix/Helper/ISettingsManager.cs @@ -7,12 +7,14 @@ namespace StabilityMatrix.Helper; public interface ISettingsManager { - public string AppDataDir { get; } - public string AppHomeDir { get; } - public string DatabasePath { get; } - Settings Settings { get; } event EventHandler? ModelBrowserNsfwEnabledChanged; + + bool IsPortableMode { get; } + string LibraryDir { get; } + string DatabasePath { get; } + bool TryFindLibrary(); + IEnumerable GetOldInstalledPackages(); void SetTheme(string theme); void AddInstalledPackage(InstalledPackage p); diff --git a/StabilityMatrix/Helper/SettingsManager.cs b/StabilityMatrix/Helper/SettingsManager.cs index 80c0319f..61be937f 100644 --- a/StabilityMatrix/Helper/SettingsManager.cs +++ b/StabilityMatrix/Helper/SettingsManager.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; +using NLog; using StabilityMatrix.Models; using Wpf.Ui.Controls.Window; @@ -12,49 +13,104 @@ namespace StabilityMatrix.Helper; public class SettingsManager : ISettingsManager { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly ReaderWriterLockSlim FileLock = new(); - /// - /// Directory of %AppData% - /// - // ReSharper disable once MemberCanBePrivate.Global - public string AppDataDir => Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); - - /// - /// Directory of %AppData%\StabilityMatrix - /// - public string AppHomeDir => Path.Combine(AppDataDir, "StabilityMatrix"); + private readonly string? originalEnvPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); - /// - /// Path to database file - /// - public string DatabasePath => Path.Combine(AppHomeDir, "StabilityMatrix.db"); + // Library properties + public bool IsPortableMode { get; set; } + public string LibraryDir { get; set; } = string.Empty; - private const string SettingsFileName = "settings.json"; - private string SettingsPath => Path.Combine(AppHomeDir, SettingsFileName); - private readonly string? originalEnvPath = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); + // Dynamic paths from library + public string DatabasePath => Path.Combine(LibraryDir, "StabilityMatrix.db"); + private string SettingsPath => Path.Combine(LibraryDir, "settings.json"); public Settings Settings { get; private set; } = new(); - +$"" public event EventHandler? ModelBrowserNsfwEnabledChanged; - - public SettingsManager() + + /// + /// Attempts to locate and set the library path + /// Return true if found, false otherwise + /// + public bool TryFindLibrary() { - if (!Directory.Exists(SettingsPath.Replace(SettingsFileName, ""))) + // 1. Check portable mode + var appDir = AppContext.BaseDirectory; + IsPortableMode = File.Exists(Path.Combine(appDir, ".sm-portable")); + if (IsPortableMode) { - Directory.CreateDirectory(SettingsPath.Replace(SettingsFileName, "")); + LibraryDir = Path.Combine(appDir, "Data"); + return true; } - - if (!File.Exists(SettingsPath)) + + // 2. Check %APPDATA%/StabilityMatrix/library.json + var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var libraryJsonPath = Path.Combine(appDataDir, "StabilityMatrix", "library.json"); + if (File.Exists(libraryJsonPath)) { - File.Create(SettingsPath).Close(); - Settings.Theme = "Dark"; - Settings.WindowBackdropType = WindowBackdropType.Mica; - var defaultSettingsJson = JsonSerializer.Serialize(Settings); - File.WriteAllText(SettingsPath, defaultSettingsJson); + try + { + var libraryJson = File.ReadAllText(libraryJsonPath); + var library = JsonSerializer.Deserialize(libraryJson); + if (!string.IsNullOrWhiteSpace(library?.LibraryPath)) + { + LibraryDir = library.LibraryPath; + return true; + } + } + catch (Exception e) + { + Logger.Warn("Failed to read library.json in AppData: {Message}", e.Message); + } } - LoadSettings(); + return false; + } + + /// + /// Save a new library path to %APPDATA%/StabilityMatrix/library.json + /// + public void SetLibraryPath(string path) + { + var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData); + var homeDir = Path.Combine(appDataDir, "StabilityMatrix"); + Directory.CreateDirectory(homeDir); + var libraryJsonPath = Path.Combine(homeDir, "library.json"); + + var library = new LibrarySettings { LibraryPath = path }; + var libraryJson = JsonSerializer.Serialize(library, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(libraryJsonPath, libraryJson); + } + + /// + /// Enable and create settings files for portable mode + /// This creates the ./Data directory and the `.sm-portable` marker file + /// + public void SetupPortableMode() + { + // Get app directory + var appDir = AppContext.BaseDirectory; + // Create data directory + var dataDir = Path.Combine(appDir, "Data"); + Directory.CreateDirectory(dataDir); + // Create marker file + File.Create(Path.Combine(dataDir, ".sm-portable")).Close(); + } + + /// + /// Iterable of installed packages using the old absolute path format. + /// Can be called with Any() to check if the user needs to migrate. + /// + public IEnumerable GetOldInstalledPackages() + { + var installed = Settings.InstalledPackages; + // Absolute paths are old formats requiring migration + foreach (var package in installed.Where(package => Path.IsPathRooted(package.Path))) + { + yield return package; + } } public void SetTheme(string theme) @@ -245,11 +301,25 @@ public class SettingsManager : ISettingsManager return Settings.SharedFolderVisibleCategories?.HasFlag(type) ?? false; } + /// + /// Loads settings from the settings file + /// If the settings file does not exist, it will be created with default values + /// private void LoadSettings() { FileLock.EnterReadLock(); try { + if (!File.Exists(SettingsPath)) + { + File.Create(SettingsPath).Close(); + Settings.Theme = "Dark"; + Settings.WindowBackdropType = WindowBackdropType.Mica; + var defaultSettingsJson = JsonSerializer.Serialize(Settings); + File.WriteAllText(SettingsPath, defaultSettingsJson); + return; + } + var settingsContent = File.ReadAllText(SettingsPath); Settings = JsonSerializer.Deserialize(settingsContent, new JsonSerializerOptions {