Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

189 lines
6.2 KiB

using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using Avalonia.Platform.Storage;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using NLog;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
[View(typeof(SelectDataDirectoryDialog))]
[ManagedService]
[Transient]
public partial class SelectDataDirectoryViewModel : ContentDialogViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public static string DefaultInstallLocation =>
Compat.IsLinux
? Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
"StabilityMatrix"
)
: Compat.AppDataHome;
private readonly ISettingsManager settingsManager;
private const string ValidExistingDirectoryText = "Valid existing data directory found";
private const string InvalidDirectoryText = "Directory must be empty or have a valid settings.json file";
private const string NotEnoughFreeSpaceText = "Not enough free space on the selected drive";
private const string FatWarningText = "FAT32 / exFAT drives are not supported at this time";
[ObservableProperty]
private string dataDirectory = DefaultInstallLocation;
[ObservableProperty]
private bool isPortableMode = Compat.IsLinux;
[ObservableProperty]
private string directoryStatusText = string.Empty;
[ObservableProperty]
private bool isStatusBadgeVisible;
[ObservableProperty]
private bool isDirectoryValid;
[ObservableProperty]
private bool showFatWarning;
public RefreshBadgeViewModel ValidatorRefreshBadge { get; } =
new()
{
State = ProgressState.Inactive,
SuccessToolTipText = ValidExistingDirectoryText,
FailToolTipText = InvalidDirectoryText
};
public SelectDataDirectoryViewModel(ISettingsManager settingsManager)
{
this.settingsManager = settingsManager;
ValidatorRefreshBadge.RefreshFunc = ValidateDataDirectory;
}
public override void OnLoaded()
{
ValidatorRefreshBadge.RefreshCommand.ExecuteAsync(null).SafeFireAndForget();
}
// Revalidate on data directory change
partial void OnDataDirectoryChanged(string value)
{
ValidatorRefreshBadge.RefreshCommand.ExecuteAsync(null).SafeFireAndForget();
}
private async Task<bool> ValidateDataDirectory()
{
await using var delay = new MinimumDelay(100, 200);
ShowFatWarning = IsDriveFat(DataDirectory);
// Doesn't exist, this is fine as a new install, hide badge
if (!Directory.Exists(DataDirectory))
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Otherwise check that a settings.json exists
var settingsPath = Path.Combine(DataDirectory, "settings.json");
// settings.json exists: Try deserializing it
if (File.Exists(settingsPath))
{
try
{
var jsonText = await File.ReadAllTextAsync(settingsPath);
var _ = JsonSerializer.Deserialize<Core.Models.Settings.Settings>(
jsonText,
new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }
);
// If successful, show existing badge
IsStatusBadgeVisible = true;
IsDirectoryValid = true;
DirectoryStatusText = ValidExistingDirectoryText;
return true;
}
catch (Exception e)
{
Logger.Info("Failed to deserialize settings.json: {Msg}", e.Message);
// If not, show error badge, and set directory to invalid to prevent continuing
IsStatusBadgeVisible = true;
IsDirectoryValid = false;
DirectoryStatusText = InvalidDirectoryText;
return false;
}
}
// No settings.json
// Check if the directory is %APPDATA%\StabilityMatrix: hide badge and set directory valid
if (DataDirectory == DefaultInstallLocation)
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Check if the directory is empty: hide badge and set directory to valid
var isEmpty = !Directory.EnumerateFileSystemEntries(DataDirectory).Any();
if (isEmpty)
{
IsStatusBadgeVisible = false;
IsDirectoryValid = true;
return true;
}
// Not empty and not appdata: show error badge, and set directory to invalid
IsStatusBadgeVisible = true;
IsDirectoryValid = false;
DirectoryStatusText = InvalidDirectoryText;
return false;
}
private bool CanPickFolder => App.StorageProvider.CanPickFolder;
[RelayCommand(CanExecute = nameof(CanPickFolder))]
private async Task ShowFolderBrowserDialog()
{
var provider = App.StorageProvider;
var result = await provider.OpenFolderPickerAsync(
new FolderPickerOpenOptions { Title = "Select Data Folder", AllowMultiple = false }
);
if (result.Count != 1)
return;
DataDirectory = result[0].Path.LocalPath;
}
partial void OnIsPortableModeChanged(bool value)
{
DataDirectory = value ? Compat.AppCurrentDir + "Data" : DefaultInstallLocation;
}
private bool IsDriveFat(string path)
{
try
{
var drive = new DriveInfo(Path.GetPathRoot(path));
return drive.DriveFormat.Contains("FAT", StringComparison.OrdinalIgnoreCase);
}
catch (Exception e)
{
Logger.Warn(e, "Error checking drive FATness");
return false;
}
}
}