diff --git a/StabilityMatrix/Api/ICivitApi.cs b/StabilityMatrix/Api/ICivitApi.cs new file mode 100644 index 00000000..472d73fa --- /dev/null +++ b/StabilityMatrix/Api/ICivitApi.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Refit; +using StabilityMatrix.Models.Api; + +namespace StabilityMatrix.Api; + +public interface ICivitApi +{ + [Get("/api/v1/models")] + Task GetModels([Body] CivitModelsRequest request); +} diff --git a/StabilityMatrix/App.xaml.cs b/StabilityMatrix/App.xaml.cs index feb43770..c6b76738 100644 --- a/StabilityMatrix/App.xaml.cs +++ b/StabilityMatrix/App.xaml.cs @@ -74,8 +74,9 @@ namespace StabilityMatrix serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddTransient(); - + serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); @@ -86,6 +87,7 @@ namespace StabilityMatrix serviceCollection.AddTransient(); serviceCollection.AddTransient(); serviceCollection.AddTransient(); + serviceCollection.AddTransient(); var settingsManager = new SettingsManager(); serviceCollection.AddSingleton(settingsManager); @@ -136,6 +138,13 @@ namespace StabilityMatrix c.Timeout = TimeSpan.FromSeconds(2); }) .AddPolicyHandler(retryPolicy); + serviceCollection.AddRefitClient(defaultRefitSettings) + .ConfigureHttpClient(c => + { + c.BaseAddress = new Uri("https://civitai.com"); + c.Timeout = TimeSpan.FromSeconds(8); + }) + .AddPolicyHandler(retryPolicy); // Logging configuration var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt"); diff --git a/StabilityMatrix/CheckpointBrowserPage.xaml b/StabilityMatrix/CheckpointBrowserPage.xaml new file mode 100644 index 00000000..fdbac4d6 --- /dev/null +++ b/StabilityMatrix/CheckpointBrowserPage.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/StabilityMatrix/CheckpointBrowserPage.xaml.cs b/StabilityMatrix/CheckpointBrowserPage.xaml.cs new file mode 100644 index 00000000..f861a421 --- /dev/null +++ b/StabilityMatrix/CheckpointBrowserPage.xaml.cs @@ -0,0 +1,20 @@ +using System.Diagnostics; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Effects; +using StabilityMatrix.ViewModels; +using Wpf.Ui.Controls; + +namespace StabilityMatrix; + +public partial class CheckpointBrowserPage : Page +{ + public CheckpointBrowserPage(CheckpointBrowserViewModel viewModel) + { + InitializeComponent(); + DataContext = viewModel; + } +} diff --git a/StabilityMatrix/CheckpointManagerPage.xaml b/StabilityMatrix/CheckpointManagerPage.xaml index 62209d12..cb57bc17 100644 --- a/StabilityMatrix/CheckpointManagerPage.xaml +++ b/StabilityMatrix/CheckpointManagerPage.xaml @@ -51,12 +51,26 @@ Header="{Binding Title}" IsExpanded="True" Margin="8"> - + + + + + + + + + + + diff --git a/StabilityMatrix/MainWindow.xaml b/StabilityMatrix/MainWindow.xaml index 9d3afb6f..fa9f5d70 100644 --- a/StabilityMatrix/MainWindow.xaml +++ b/StabilityMatrix/MainWindow.xaml @@ -7,13 +7,13 @@ Title="Stability Matrix" Width="1100" WindowBackdropType="Acrylic" - d:DesignHeight="500" - d:DesignWidth="700" d:DataContext="{d:DesignInstance Type=viewModels:MainWindowViewModel, IsDesignTimeCreatable=True}" + d:DesignHeight="500" + d:DesignWidth="700" + mc:Ignorable="d" ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}" ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}" - mc:Ignorable="d" x:Class="StabilityMatrix.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" @@ -69,6 +69,11 @@ + + + + + @@ -89,7 +94,7 @@ - + diff --git a/StabilityMatrix/Models/Api/CivitCommercialUse.cs b/StabilityMatrix/Models/Api/CivitCommercialUse.cs new file mode 100644 index 00000000..0384bc95 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitCommercialUse.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum CivitCommercialUse +{ + None, + Image, + Rent, + Sell +} diff --git a/StabilityMatrix/Models/Api/CivitCreator.cs b/StabilityMatrix/Models/Api/CivitCreator.cs new file mode 100644 index 00000000..bd37ff5d --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitCreator.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitCreator +{ + [JsonPropertyName("username")] + public string Username { get; set; } + + [JsonPropertyName("image")] + public string? Image { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitFile.cs b/StabilityMatrix/Models/Api/CivitFile.cs new file mode 100644 index 00000000..de211641 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitFile.cs @@ -0,0 +1,22 @@ +using System; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitFile +{ + [JsonPropertyName("sizeKb")] + public double SizeKb { get; set; } + + [JsonPropertyName("pickleScanResult")] + public string PickleScanResult { get; set; } + + [JsonPropertyName("virusScanResult")] + public string VirusScanResult { get; set; } + + [JsonPropertyName("scannedAt")] + public DateTime? ScannedAt { get; set; } + + [JsonPropertyName("metadata")] + public CivitFileMetadata Metadata { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitFileMetadata.cs b/StabilityMatrix/Models/Api/CivitFileMetadata.cs new file mode 100644 index 00000000..17fc3174 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitFileMetadata.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitFileMetadata +{ + [JsonPropertyName("fp")] + public CivitModelFpType? Fp { get; set; } + + [JsonPropertyName("size")] + public CivitModelSize? Size { get; set; } + + [JsonPropertyName("format")] + public CivitModelFormat? Format { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitImage.cs b/StabilityMatrix/Models/Api/CivitImage.cs new file mode 100644 index 00000000..efe86599 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitImage.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitImage +{ + [JsonPropertyName("url")] + public string Url { get; set; } + + [JsonPropertyName("nsfw")] + public string Nsfw { get; set; } + + [JsonPropertyName("width")] + public int Width { get; set; } + + [JsonPropertyName("height")] + public int Height { get; set; } + + [JsonPropertyName("hash")] + public string Hash { get; set; } + + // TODO: "meta" ( object? ) +} diff --git a/StabilityMatrix/Models/Api/CivitMetadata.cs b/StabilityMatrix/Models/Api/CivitMetadata.cs new file mode 100644 index 00000000..b4bfdbd0 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitMetadata.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + + +public class CivitMetadata +{ + [JsonPropertyName("totalItems")] + public int TotalItems { get; set; } + + [JsonPropertyName("currentPage")] + public int CurrentPage { get; set; } + + [JsonPropertyName("pageSize")] + public int PageSize { get; set; } + + [JsonPropertyName("totalPages")] + public int TotalPages { get; set; } + + [JsonPropertyName("nextPage")] + public string? NextPage { get; set; } + + [JsonPropertyName("prevPage")] + public string? PrevPage { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitMode.cs b/StabilityMatrix/Models/Api/CivitMode.cs new file mode 100644 index 00000000..b79273c0 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitMode.cs @@ -0,0 +1,10 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum CivitMode +{ + Archived, + TakenDown +} diff --git a/StabilityMatrix/Models/Api/CivitModel.cs b/StabilityMatrix/Models/Api/CivitModel.cs new file mode 100644 index 00000000..1058ed98 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModel.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitModel +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("type")] + public CivitModelType Type { get; set; } + + [JsonPropertyName("nsfw")] + public bool Nsfw { get; set; } + + [JsonPropertyName("tags")] + public string[] Tags { get; set; } + + [JsonPropertyName("mode")] + public CivitMode? Mode { get; set; } + + [JsonPropertyName("creator")] + public CivitCreator Creator { get; set; } + + [JsonPropertyName("stats")] + public CivitModelStats Stats { get; set; } + + [JsonPropertyName("modelVersions")] + public CivitModelVersion[] ModelVersions { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitModelFormat.cs b/StabilityMatrix/Models/Api/CivitModelFormat.cs new file mode 100644 index 00000000..60ca5b2a --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelFormat.cs @@ -0,0 +1,14 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + + +[JsonConverter(typeof(JsonStringEnumConverter))] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum CivitModelFormat +{ + SafeTensor, + PickleTensor, + Other +} diff --git a/StabilityMatrix/Models/Api/CivitModelFpType.cs b/StabilityMatrix/Models/Api/CivitModelFpType.cs new file mode 100644 index 00000000..acef4452 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelFpType.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + + +[JsonConverter(typeof(JsonStringEnumConverter))] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum CivitModelFpType +{ + fp16, + fp32 +} diff --git a/StabilityMatrix/Models/Api/CivitModelSize.cs b/StabilityMatrix/Models/Api/CivitModelSize.cs new file mode 100644 index 00000000..5d78c753 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelSize.cs @@ -0,0 +1,12 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum CivitModelSize +{ + full, + pruned, +} diff --git a/StabilityMatrix/Models/Api/CivitModelStats.cs b/StabilityMatrix/Models/Api/CivitModelStats.cs new file mode 100644 index 00000000..8f52a991 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelStats.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitModelStats : CivitStats +{ + [JsonPropertyName("favoriteCount")] + public int FavoriteCount { get; set; } + + [JsonPropertyName("commentCount")] + public int CommentCount { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitModelType.cs b/StabilityMatrix/Models/Api/CivitModelType.cs new file mode 100644 index 00000000..f835bb52 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelType.cs @@ -0,0 +1,17 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public enum CivitModelType +{ + Checkpoint, + TextualInversion, + Hypernetwork, + AestheticGradient, + LORA, + Controlnet, + Poses +} diff --git a/StabilityMatrix/Models/Api/CivitModelVersion.cs b/StabilityMatrix/Models/Api/CivitModelVersion.cs new file mode 100644 index 00000000..857827c6 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelVersion.cs @@ -0,0 +1,34 @@ +using System; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitModelVersion +{ + [JsonPropertyName("id")] + public int Id { get; set; } + + [JsonPropertyName("name")] + public string Name { get; set; } + + [JsonPropertyName("description")] + public string Description { get; set; } + + [JsonPropertyName("createdAt")] + public DateTime CreatedAt { get; set; } + + [JsonPropertyName("downloadUrl")] + public string DownloadUrl { get; set; } + + [JsonPropertyName("trainedWords")] + public string[] TrainedWords { get; set; } + + [JsonPropertyName("files")] + public CivitFile[] Files { get; set; } + + [JsonPropertyName("images")] + public CivitImage[] Images { get; set; } + + [JsonPropertyName("stats")] + public CivitModelStats Stats { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitModelsRequest.cs b/StabilityMatrix/Models/Api/CivitModelsRequest.cs new file mode 100644 index 00000000..1160ba90 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelsRequest.cs @@ -0,0 +1,105 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + + +public class CivitModelsRequest +{ + /// + /// The number of results to be returned per page. This can be a number between 1 and 200. By default, each page will return 100 results + /// + [JsonPropertyName("limit")] + public int? Limit { get; set; } + + /// + /// The page from which to start fetching models + /// + [JsonPropertyName("page")] + public int? Page { get; set; } + + /// + /// Search query to filter models by name + /// + [JsonPropertyName("query")] + public string? Query { get; set; } + + /// + /// Search query to filter models by tag + /// + [JsonPropertyName("tag")] + public string? Tag { get; set; } + + /// + /// Search query to filter models by user + /// + [JsonPropertyName("username")] + public string? Username { get; set; } + + /// + /// The type of model you want to filter with. If none is specified, it will return all types + /// + [JsonPropertyName("types")] + public CivitModelType[]? Types { get; set; } + + /// + /// The order in which you wish to sort the results + /// + [JsonPropertyName("sort")] + public CivitSortMode? Sort { get; set; } + + /// + /// The time frame in which the models will be sorted + /// + [JsonPropertyName("period")] + public CivitPeriod? Period { get; set; } + + /// + /// The rating you wish to filter the models with. If none is specified, it will return models with any rating + /// + [JsonPropertyName("rating")] + public int? Rating { get; set; } + + /// + /// Filter to models that require or don't require crediting the creator + /// Requires Authentication + /// + [JsonPropertyName("favorites")] + public bool? Favorites { get; set; } + + /// + /// Filter to hidden models of the authenticated user + /// Requires Authentication + /// + [JsonPropertyName("hidden")] + public bool? Hidden { get; set; } + + /// + /// Only include the primary file for each model (This will use your preferred format options if you use an API token or session cookie) + /// + [JsonPropertyName("primaryFileOnly")] + public bool? PrimaryFileOnly { get; set; } + + /// + /// Filter to models that allow or don't allow creating derivatives + /// + [JsonPropertyName("allowDerivatives")] + public bool? AllowDerivatives { get; set; } + + /// + /// Filter to models that allow or don't allow derivatives to have a different license + /// + [JsonPropertyName("allowDifferentLicenses")] + public bool? AllowDifferentLicenses { get; set; } + + /// + /// Filter to models based on their commercial permissions + /// + [JsonPropertyName("allowCommercialUse")] + public CivitCommercialUse? AllowCommercialUse { get; set; } + + /// + /// If false, will return safer images and hide models that don't have safe images + /// + [JsonPropertyName("nsfw")] + public bool? Nsfw { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitModelsResponse.cs b/StabilityMatrix/Models/Api/CivitModelsResponse.cs new file mode 100644 index 00000000..7a51c422 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitModelsResponse.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitModelsResponse +{ + [JsonPropertyName("items")] + public CivitModel[] Items { get; set; } + + [JsonPropertyName("metadata")] + public CivitMetadata Metadata { get; set; } +} diff --git a/StabilityMatrix/Models/Api/CivitPeriod.cs b/StabilityMatrix/Models/Api/CivitPeriod.cs new file mode 100644 index 00000000..222629bb --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitPeriod.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum CivitPeriod +{ + AllTime, + Year, + Month, + Week, + Day +} diff --git a/StabilityMatrix/Models/Api/CivitSortMode.cs b/StabilityMatrix/Models/Api/CivitSortMode.cs new file mode 100644 index 00000000..4ea22673 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitSortMode.cs @@ -0,0 +1,16 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum CivitSortMode +{ + [EnumMember(Value = "Highest Rated")] + HighestRated, + [EnumMember(Value = "Most Downloaded")] + MostDownloaded, + [EnumMember(Value = "Newest")] + Newest +} diff --git a/StabilityMatrix/Models/Api/CivitStats.cs b/StabilityMatrix/Models/Api/CivitStats.cs new file mode 100644 index 00000000..69c37420 --- /dev/null +++ b/StabilityMatrix/Models/Api/CivitStats.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace StabilityMatrix.Models.Api; + +public class CivitStats +{ + [JsonPropertyName("downloadCount")] + public int DownloadCount { get; set; } + + [JsonPropertyName("ratingCount")] + public int RatingCount { get; set; } + + [JsonPropertyName("rating")] + public double Rating { get; set; } +} diff --git a/StabilityMatrix/StabilityMatrix.csproj b/StabilityMatrix/StabilityMatrix.csproj index 19867f0b..2ddb3a5d 100644 --- a/StabilityMatrix/StabilityMatrix.csproj +++ b/StabilityMatrix/StabilityMatrix.csproj @@ -81,6 +81,11 @@ Wpf Designer + + MSBuild:Compile + Wpf + Designer + diff --git a/StabilityMatrix/ViewModels/CheckpointBrowserViewModel.cs b/StabilityMatrix/ViewModels/CheckpointBrowserViewModel.cs new file mode 100644 index 00000000..78e1068f --- /dev/null +++ b/StabilityMatrix/ViewModels/CheckpointBrowserViewModel.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using NLog; +using StabilityMatrix.Api; +using StabilityMatrix.Models.Api; + +namespace StabilityMatrix.ViewModels; + +public partial class CheckpointBrowserViewModel : ObservableObject +{ + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + private readonly ICivitApi civitApi; + + [ObservableProperty] + private string? searchQuery; + + public CheckpointBrowserViewModel(ICivitApi civitApi) + { + this.civitApi = civitApi; + } + + [RelayCommand] + private async Task SearchModels() + { + if (string.IsNullOrWhiteSpace(SearchQuery)) + { + return; + } + + var models = await civitApi.GetModels(new CivitModelsRequest + { + Query = SearchQuery, + Limit = 5, + Nsfw = true, + }); + + Logger.Debug($"Found {models.Items.Length} models"); + } + +}