From e11f3d36c37c46d92345e055cc78d6fdc51f9ccf Mon Sep 17 00:00:00 2001 From: Ionite Date: Mon, 5 Jun 2023 19:04:13 -0400 Subject: [PATCH] Add ConnectedModelInfo, deletion progress --- StabilityMatrix/CheckpointManagerPage.xaml | 127 ++++++++++++++---- .../BooleanToHiddenVisibleConverter.cs | 32 +++++ StabilityMatrix/Models/CheckpointFile.cs | 79 ++++++++--- StabilityMatrix/Models/ConnectedModelInfo.cs | 49 +++++++ 4 files changed, 243 insertions(+), 44 deletions(-) create mode 100644 StabilityMatrix/Converters/BooleanToHiddenVisibleConverter.cs create mode 100644 StabilityMatrix/Models/ConnectedModelInfo.cs diff --git a/StabilityMatrix/CheckpointManagerPage.xaml b/StabilityMatrix/CheckpointManagerPage.xaml index 9b019254..77da1393 100644 --- a/StabilityMatrix/CheckpointManagerPage.xaml +++ b/StabilityMatrix/CheckpointManagerPage.xaml @@ -9,6 +9,7 @@ mc:Ignorable="d" x:Class="StabilityMatrix.CheckpointManagerPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:converters="clr-namespace:StabilityMatrix.Converters" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:designData="clr-namespace:StabilityMatrix.DesignData" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" @@ -19,6 +20,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + + + + + - - + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -173,7 +246,7 @@ HorizontalAlignment="Stretch" ItemTemplate="{StaticResource CheckpointFolderGridDataTemplate}" ItemsSource="{Binding CheckpointFolders, Mode=OneWay}" - Margin="16,16,16,16" /> + Margin="8" /> diff --git a/StabilityMatrix/Converters/BooleanToHiddenVisibleConverter.cs b/StabilityMatrix/Converters/BooleanToHiddenVisibleConverter.cs new file mode 100644 index 00000000..13e69d46 --- /dev/null +++ b/StabilityMatrix/Converters/BooleanToHiddenVisibleConverter.cs @@ -0,0 +1,32 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace StabilityMatrix.Converters; + +public class BooleanToHiddenVisibleConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var bValue = false; + if (value is bool b) + { + bValue = b; + } + else if (value is bool) + { + var tmp = (bool?) value; + bValue = tmp.Value; + } + return bValue ? Visibility.Visible : Visibility.Hidden; + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is Visibility visibility) + { + return visibility == Visibility.Visible; + } + return false; + } +} diff --git a/StabilityMatrix/Models/CheckpointFile.cs b/StabilityMatrix/Models/CheckpointFile.cs index fac5dbe9..ad83f1ad 100644 --- a/StabilityMatrix/Models/CheckpointFile.cs +++ b/StabilityMatrix/Models/CheckpointFile.cs @@ -1,64 +1,93 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows.Media.Imaging; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using NLog; namespace StabilityMatrix.Models; public partial class CheckpointFile : ObservableObject { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); // Event for when this file is deleted public event EventHandler? Deleted; - + /// /// Absolute path to the checkpoint file. /// public string FilePath { get; init; } = string.Empty; - + /// /// Custom title for UI. /// - public string Title { get; init; } = string.Empty; - - public string? PreviewImagePath { get; set; } + [ObservableProperty] private string title = string.Empty; + public string? PreviewImagePath { get; set; } public BitmapImage? PreviewImage { get; set; } - public bool IsPreviewImageLoaded => PreviewImage != null; + + [ObservableProperty] private ConnectedModelInfo? connectedModel; + public bool IsConnectedModel => ConnectedModel != null; + + [ObservableProperty] private bool isLoading; public string FileName => Path.GetFileName(FilePath); private static readonly string[] SupportedCheckpointExtensions = { ".safetensors", ".pt", ".ckpt", ".pth" }; private static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; + partial void OnConnectedModelChanged(ConnectedModelInfo? value) + { + if (value == null) return; + // Update title, first check user defined, then connected model name + Title = value.UserTitle ?? value.ModelName; + } + [RelayCommand] - public void Delete() + private async Task DeleteAsync() { if (File.Exists(FilePath)) { - Task.Run(() => + // Start progress ring + IsLoading = true; + var timer = Stopwatch.StartNew(); + try { - File.Delete(FilePath); - Deleted?.Invoke(this, this); - }); - } - - if (PreviewImagePath != null && File.Exists(PreviewImagePath)) - { - Task.Run(() => File.Delete(PreviewImagePath)); + await Task.Run(() => File.Delete(FilePath)); + if (PreviewImagePath != null && File.Exists(PreviewImagePath)) + { + await Task.Run(() => File.Delete(PreviewImagePath)); + } + // If it was too fast, wait a bit to show progress ring + var targetDelay = new Random().Next(200, 500); + var elapsed = timer.ElapsedMilliseconds; + if (elapsed < targetDelay) + { + await Task.Delay(targetDelay - (int) elapsed); + } + } + catch (IOException e) + { + Logger.Error(e, $"Failed to delete checkpoint file: {FilePath}"); + IsLoading = false; + return; // Don't delete from collection + } } + Deleted?.Invoke(this, this); } /// /// Indexes directory and yields all checkpoint files. /// First we match all files with supported extensions. /// If found, we also look for - /// - {filename}.preview.{image-extensions} + /// - {filename}.preview.{image-extensions} (preview image) + /// - {filename}.cm-info.json (connected model info) /// public static IEnumerable FromDirectoryIndex(string directory, SearchOption searchOption = SearchOption.TopDirectoryOnly) { @@ -75,6 +104,22 @@ public partial class CheckpointFile : ObservableObject Title = Path.GetFileNameWithoutExtension(file), FilePath = Path.Combine(directory, file), }; + + // Check for connected model info + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file); + var cmInfoPath = $"{fileNameWithoutExtension}.cm-info.json"; + if (files.ContainsKey(cmInfoPath)) + { + try + { + var jsonData = File.ReadAllText(Path.Combine(directory, cmInfoPath)); + checkpointFile.ConnectedModel = ConnectedModelInfo.FromJson(jsonData); + } + catch (IOException e) + { + Debug.WriteLine($"Failed to parse {cmInfoPath}: {e}"); + } + } // Check for preview image var previewImage = SupportedImageExtensions.Select(ext => $"{checkpointFile.FileName}.preview.{ext}").FirstOrDefault(files.ContainsKey); diff --git a/StabilityMatrix/Models/ConnectedModelInfo.cs b/StabilityMatrix/Models/ConnectedModelInfo.cs new file mode 100644 index 00000000..245feae2 --- /dev/null +++ b/StabilityMatrix/Models/ConnectedModelInfo.cs @@ -0,0 +1,49 @@ +using System; +using System.Text.Json; +using StabilityMatrix.Extensions; +using StabilityMatrix.Models.Api; + +namespace StabilityMatrix.Models; + +public class ConnectedModelInfo +{ + public int ModelId { get; set; } + public string ModelName { get; set; } + public string ModelDescription { get; set; } + public bool Nsfw { get; set; } + public string[] Tags { get; set; } + public CivitModelType ModelType { get; set; } + public int VersionId { get; set; } + public string VersionName { get; set; } + public string VersionDescription { get; set; } + public string? BaseModel { get; set; } + public CivitFileMetadata FileMetadata { get; set; } + public DateTime ImportedAt { get; set; } + public CivitFileHashes Hashes { get; set; } + + // User settings + public string? UserTitle { get; set; } + public string? ThumbnailImageUrl { get; set; } + + public ConnectedModelInfo(CivitModel civitModel, CivitModelVersion civitModelVersion, CivitFile civitFile, DateTime importedAt) + { + ModelId = civitModel.Id; + ModelName = civitModel.Name; + ModelDescription = civitModel.Description; + Nsfw = civitModel.Nsfw; + Tags = civitModel.Tags; + ModelType = civitModel.Type; + VersionId = civitModelVersion.Id; + VersionName = civitModelVersion.Name; + VersionDescription = civitModelVersion.Description; + ImportedAt = importedAt; + BaseModel = civitModelVersion.BaseModel; + FileMetadata = civitFile.Metadata; + Hashes = civitFile.Hashes; + } + + public static ConnectedModelInfo? FromJson(string json) + { + return JsonSerializer.Deserialize(json); + } +}