Browse Source

Merge branch 'main' into setup-dependencies

pull/5/head^2
Ionite 1 year ago committed by GitHub
parent
commit
5b71578909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      StabilityMatrix/App.xaml.cs
  2. 11
      StabilityMatrix/CheckpointBrowserPage.xaml
  3. 41
      StabilityMatrix/Converters/Json/DefaultUnknownEnumConverter.cs
  4. 3
      StabilityMatrix/Helper/IPrerequisiteHelper.cs
  5. 7
      StabilityMatrix/Helper/PrerequisiteHelper.cs
  6. 4
      StabilityMatrix/Models/Api/CivitModelType.cs
  7. 23
      StabilityMatrix/SettingsPage.xaml
  8. 78
      StabilityMatrix/ViewModels/CheckpointBrowserViewModel.cs
  9. 17
      StabilityMatrix/ViewModels/SettingsViewModel.cs

9
StabilityMatrix/App.xaml.cs

@ -18,7 +18,6 @@ using Microsoft.Extensions.Logging;
using NLog; using NLog;
using NLog.Config; using NLog.Config;
using NLog.Extensions.Logging; using NLog.Extensions.Logging;
using NLog.Layouts;
using NLog.Targets; using NLog.Targets;
using Octokit; using Octokit;
using Polly; using Polly;
@ -361,9 +360,13 @@ namespace StabilityMatrix
}; };
var exceptionWindow = new ExceptionWindow var exceptionWindow = new ExceptionWindow
{ {
DataContext = vm, DataContext = vm
Owner = MainWindow
}; };
if (MainWindow?.IsActive ?? false)
{
exceptionWindow.Owner = MainWindow;
}
exceptionWindow.ShowDialog(); exceptionWindow.ShowDialog();
} }
e.Handled = true; e.Handled = true;

11
StabilityMatrix/CheckpointBrowserPage.xaml

@ -139,7 +139,7 @@
<ui:TextBox <ui:TextBox
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="8,0,0,0" Margin="8,0,0,0"
PlaceholderText="Query" PlaceholderText="Search models, #tags, or @users"
Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> Text="{Binding SearchQuery, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<ui:Button <ui:Button
@ -238,6 +238,15 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
<TextBlock
Grid.Row="0"
Grid.RowSpan="3"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding NoResultsText, FallbackValue=No results found}"
Visibility="{Binding NoResultsFound, Converter={StaticResource BoolToVisibilityConverter}}" />
<ui:ProgressRing <ui:ProgressRing
Grid.Row="0" Grid.Row="0"
Grid.RowSpan="3" Grid.RowSpan="3"

41
StabilityMatrix/Converters/Json/DefaultUnknownEnumConverter.cs

@ -0,0 +1,41 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using StabilityMatrix.Extensions;
namespace StabilityMatrix.Converters.Json;
public class DefaultUnknownEnumConverter<T> : JsonConverter<T> where T : Enum
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.String)
{
throw new JsonException();
}
var enumText = reader.GetString();
if (Enum.TryParse(typeof(T), enumText, true, out var result))
{
return (T) result!;
}
// Unknown value handling
if (Enum.TryParse(typeof(T), "Unknown", true, out var unknownResult))
{
return (T) unknownResult!;
}
throw new JsonException($"Unable to parse '{enumText}' to enum '{typeof(T)}'.");
}
public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
return;
}
writer.WriteStringValue(value.GetStringValue());
}
}

3
StabilityMatrix/Helper/IPrerequisiteHelper.cs

@ -8,10 +8,13 @@ public interface IPrerequisiteHelper
{ {
string GitBinPath { get; } string GitBinPath { get; }
bool IsPythonInstalled { get; }
Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null); Task InstallAllIfNecessary(IProgress<ProgressReport>? progress = null);
Task UnpackResourcesIfNecessary(IProgress<ProgressReport>? progress = null); Task UnpackResourcesIfNecessary(IProgress<ProgressReport>? progress = null);
Task InstallGitIfNecessary(IProgress<ProgressReport>? progress = null); Task InstallGitIfNecessary(IProgress<ProgressReport>? progress = null);
Task InstallVcRedistIfNecessary(IProgress<ProgressReport>? progress = null); Task InstallVcRedistIfNecessary(IProgress<ProgressReport>? progress = null);
Task InstallPythonIfNecessary(IProgress<ProgressReport>? progress = null);
/// <summary> /// <summary>
/// Run embedded git with the given arguments. /// Run embedded git with the given arguments.

7
StabilityMatrix/Helper/PrerequisiteHelper.cs

@ -43,6 +43,8 @@ public class PrerequisiteHelper : IPrerequisiteHelper
private string GitExePath => Path.Combine(PortableGitInstallDir, "bin", "git.exe"); private string GitExePath => Path.Combine(PortableGitInstallDir, "bin", "git.exe");
public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin"); public string GitBinPath => Path.Combine(PortableGitInstallDir, "bin");
public bool IsPythonInstalled => File.Exists(PythonDllPath);
public PrerequisiteHelper(ILogger<PrerequisiteHelper> logger, IGitHubClient gitHubClient, public PrerequisiteHelper(ILogger<PrerequisiteHelper> logger, IGitHubClient gitHubClient,
IDownloadService downloadService, ISettingsManager settingsManager) IDownloadService downloadService, ISettingsManager settingsManager)
{ {
@ -215,9 +217,8 @@ public class PrerequisiteHelper : IPrerequisiteHelper
logger.LogInformation("Git not found at {GitExePath}, downloading...", GitExePath); logger.LogInformation("Git not found at {GitExePath}, downloading...", GitExePath);
var latestRelease = await gitHubClient.Repository.Release.GetLatest("git-for-windows", "git"); var portableGitUrl =
var portableGitUrl = latestRelease.Assets "https://github.com/git-for-windows/git/releases/download/v2.41.0.windows.1/PortableGit-2.41.0-64-bit.7z.exe";
.First(a => a.Name.EndsWith("64-bit.7z.exe")).BrowserDownloadUrl;
if (!File.Exists(PortableGitDownloadPath)) if (!File.Exists(PortableGitDownloadPath))
{ {

4
StabilityMatrix/Models/Api/CivitModelType.cs

@ -1,13 +1,15 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using StabilityMatrix.Converters.Json;
using StabilityMatrix.Extensions; using StabilityMatrix.Extensions;
namespace StabilityMatrix.Models.Api; namespace StabilityMatrix.Models.Api;
[JsonConverter(typeof(JsonStringEnumConverter))] [JsonConverter(typeof(DefaultUnknownEnumConverter<CivitModelType>))]
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
public enum CivitModelType public enum CivitModelType
{ {
Unknown,
[ConvertTo<SharedFolderType>(SharedFolderType.StableDiffusion)] [ConvertTo<SharedFolderType>(SharedFolderType.StableDiffusion)]
Checkpoint, Checkpoint,
[ConvertTo<SharedFolderType>(SharedFolderType.TextualInversion)] [ConvertTo<SharedFolderType>(SharedFolderType.TextualInversion)]

23
StabilityMatrix/SettingsPage.xaml

@ -133,10 +133,25 @@
FontWeight="Bold" FontWeight="Bold"
Margin="0,8" Margin="0,8"
Text="Embedded Python" /> Text="Embedded Python" />
<Button <StackPanel Orientation="Horizontal">
Command="{Binding PythonVersionCommand}" <ui:Button
Content="Check Version Info" Command="{Binding PythonVersionCommand}"
Margin="8" /> Content="Check Version Info"
Margin="8" />
<!-- Progress for python install if needed -->
<StackPanel Orientation="Horizontal" Visibility="{Binding IsPythonInstalling, Converter={StaticResource BoolToVisibilityConverter}, FallbackValue=Collapsed}">
<ui:ProgressRing
Height="24"
IsEnabled="{Binding IsPythonInstalling}"
IsIndeterminate="True"
Margin="8"
Width="24" />
<TextBlock
Margin="4"
Text="Preparing Environment"
VerticalAlignment="Center" />
</StackPanel>
</StackPanel>
</StackPanel> </StackPanel>
</ui:Card> </ui:Card>

78
StabilityMatrix/ViewModels/CheckpointBrowserViewModel.cs

@ -46,13 +46,15 @@ public partial class CheckpointBrowserViewModel : ObservableObject
[ObservableProperty] private bool canGoToNextPage; [ObservableProperty] private bool canGoToNextPage;
[ObservableProperty] private bool canGoToPreviousPage; [ObservableProperty] private bool canGoToPreviousPage;
[ObservableProperty] private bool isIndeterminate; [ObservableProperty] private bool isIndeterminate;
[ObservableProperty] private bool noResultsFound;
[ObservableProperty] private string noResultsText;
public IEnumerable<CivitPeriod> AllCivitPeriods => Enum.GetValues(typeof(CivitPeriod)).Cast<CivitPeriod>(); public IEnumerable<CivitPeriod> AllCivitPeriods => Enum.GetValues(typeof(CivitPeriod)).Cast<CivitPeriod>();
public IEnumerable<CivitSortMode> AllSortModes => Enum.GetValues(typeof(CivitSortMode)).Cast<CivitSortMode>(); public IEnumerable<CivitSortMode> AllSortModes => Enum.GetValues(typeof(CivitSortMode)).Cast<CivitSortMode>();
public IEnumerable<CivitModelType> AllModelTypes => Enum.GetValues(typeof(CivitModelType)) public IEnumerable<CivitModelType> AllModelTypes => Enum.GetValues(typeof(CivitModelType))
.Cast<CivitModelType>() .Cast<CivitModelType>()
.Where(t => t != CivitModelType.AestheticGradient && t != CivitModelType.Poses) .Where(t => t == CivitModelType.All || t.ConvertTo<SharedFolderType>() > 0)
.OrderBy(t => t.ToString()); .OrderBy(t => t.ToString());
public CheckpointBrowserViewModel( public CheckpointBrowserViewModel(
@ -100,10 +102,25 @@ public partial class CheckpointBrowserViewModel : ObservableObject
var models = modelsResponse.Items; var models = modelsResponse.Items;
if (models is null) if (models is null)
{ {
Logger.Debug("CivitAI Query {Text} returned no results (in {Elapsed:F1} s)", queryText, timer.Elapsed.TotalSeconds); Logger.Debug("CivitAI Query {Text} returned no results (in {Elapsed:F1} s)",
queryText, timer.Elapsed.TotalSeconds);
return; return;
} }
Logger.Debug("CivitAI Query {Text} returned {Results} results (in {Elapsed:F1} s)", queryText, models.Count, timer.Elapsed.TotalSeconds);
Logger.Debug("CivitAI Query {Text} returned {Results} results (in {Elapsed:F1} s)",
queryText, models.Count, timer.Elapsed.TotalSeconds);
var unknown = models.Where(m => m.Type == CivitModelType.Unknown).ToList();
if (unknown.Any())
{
var names = unknown.Select(m => m.Name).ToList();
Logger.Warn("Excluded {Unknown} unknown model types: {Models}", unknown.Count,
names);
}
// Filter out unknown model types
models = models.Where(m => m.Type.ConvertTo<SharedFolderType>() > 0).ToList();
// Database update calls will invoke `OnModelsUpdated` // Database update calls will invoke `OnModelsUpdated`
// Add to database // Add to database
await liteDbContext.UpsertCivitModelAsync(models); await liteDbContext.UpsertCivitModelAsync(models);
@ -133,6 +150,11 @@ public partial class CheckpointBrowserViewModel : ObservableObject
"CivitAI can't be reached right now").SafeFireAndForget(); "CivitAI can't be reached right now").SafeFireAndForget();
Logger.Log(LogLevel.Error, e); Logger.Log(LogLevel.Error, e);
} }
finally
{
ShowMainLoadingSpinner = false;
UpdateResultsText();
}
} }
/// <summary> /// <summary>
@ -160,17 +182,24 @@ public partial class CheckpointBrowserViewModel : ObservableObject
HasSearched = true; HasSearched = true;
} }
private string previousSearchQuery = string.Empty;
[RelayCommand] [RelayCommand]
private async Task SearchModels() private async Task SearchModels()
{ {
if (string.IsNullOrWhiteSpace(SearchQuery)) return; if (string.IsNullOrWhiteSpace(SearchQuery)) return;
var timer = Stopwatch.StartNew(); var timer = Stopwatch.StartNew();
if (SearchQuery != previousSearchQuery)
{
// Reset page number
CurrentPageNumber = 1;
previousSearchQuery = SearchQuery;
}
// Build request // Build request
var modelRequest = new CivitModelsRequest var modelRequest = new CivitModelsRequest
{ {
Query = SearchQuery,
Limit = MaxModelsPerPage, Limit = MaxModelsPerPage,
Nsfw = "true", // Handled by local view filter Nsfw = "true", // Handled by local view filter
Sort = SortMode, Sort = SortMode,
@ -178,6 +207,19 @@ public partial class CheckpointBrowserViewModel : ObservableObject
Page = CurrentPageNumber, Page = CurrentPageNumber,
}; };
if (SearchQuery.StartsWith("#"))
{
modelRequest.Tag = SearchQuery[1..];
}
else if (SearchQuery.StartsWith("@"))
{
modelRequest.Username = SearchQuery[1..];
}
else
{
modelRequest.Query = SearchQuery;
}
if (SelectedModelType != CivitModelType.All) if (SelectedModelType != CivitModelType.All)
{ {
modelRequest.Types = new[] {SelectedModelType}; modelRequest.Types = new[] {SelectedModelType};
@ -189,7 +231,7 @@ public partial class CheckpointBrowserViewModel : ObservableObject
.FindByIdAsync(ObjectHash.GetMd5Guid(modelRequest)); .FindByIdAsync(ObjectHash.GetMd5Guid(modelRequest));
// If cached, update model cards // If cached, update model cards
if (cachedQuery?.Items is not null && cachedQuery.Items.Any()) if (cachedQuery is not null)
{ {
var elapsed = timer.Elapsed; var elapsed = timer.Elapsed;
Logger.Debug("Using cached query for {Text} [{RequestHash}] (in {Elapsed:F1} s)", Logger.Debug("Using cached query for {Text} [{RequestHash}] (in {Elapsed:F1} s)",
@ -199,14 +241,13 @@ public partial class CheckpointBrowserViewModel : ObservableObject
// Start remote query (background mode) // Start remote query (background mode)
// Skip when last query was less than 2 min ago // Skip when last query was less than 2 min ago
var timeSinceCache = DateTimeOffset.UtcNow - cachedQuery.InsertedAt; var timeSinceCache = DateTimeOffset.UtcNow - cachedQuery.InsertedAt;
if (timeSinceCache?.TotalMinutes < 2) if (timeSinceCache?.TotalMinutes >= 2)
{ {
Logger.Debug("Cached query was less than 2 minutes ago ({Seconds:F0} s), skipping remote query", CivitModelQuery(modelRequest).SafeFireAndForget();
Logger.Debug(
"Cached query was more than 2 minutes ago ({Seconds:F0} s), updating cache with remote query",
timeSinceCache.Value.TotalSeconds); timeSinceCache.Value.TotalSeconds);
return;
} }
CivitModelQuery(modelRequest).SafeFireAndForget();
} }
else else
{ {
@ -214,6 +255,8 @@ public partial class CheckpointBrowserViewModel : ObservableObject
ShowMainLoadingSpinner = true; ShowMainLoadingSpinner = true;
await CivitModelQuery(modelRequest); await CivitModelQuery(modelRequest);
} }
UpdateResultsText();
} }
[RelayCommand] [RelayCommand]
@ -251,6 +294,11 @@ public partial class CheckpointBrowserViewModel : ObservableObject
{ {
settingsManager.SetModelBrowserNsfwEnabled(value); settingsManager.SetModelBrowserNsfwEnabled(value);
ModelCardsView?.Refresh(); ModelCardsView?.Refresh();
if (!HasSearched)
return;
UpdateResultsText();
} }
partial void OnSelectedPeriodChanged(CivitPeriod oldValue, CivitPeriod newValue) partial void OnSelectedPeriodChanged(CivitPeriod oldValue, CivitPeriod newValue)
@ -281,4 +329,12 @@ public partial class CheckpointBrowserViewModel : ObservableObject
// execute command instead of calling method directly so that the IsRunning property gets updated // execute command instead of calling method directly so that the IsRunning property gets updated
await SearchModelsCommand.ExecuteAsync(null); await SearchModelsCommand.ExecuteAsync(null);
} }
private void UpdateResultsText()
{
NoResultsFound = ModelCardsView?.IsEmpty ?? true;
NoResultsText = ModelCards?.Count > 0
? $"{ModelCards.Count} results hidden by filters"
: "No results found";
}
} }

17
StabilityMatrix/ViewModels/SettingsViewModel.cs

@ -41,6 +41,7 @@ public partial class SettingsViewModel : ObservableObject
private readonly IPyRunner pyRunner; private readonly IPyRunner pyRunner;
private readonly ISnackbarService snackbarService; private readonly ISnackbarService snackbarService;
private readonly ILiteDbContext liteDbContext; private readonly ILiteDbContext liteDbContext;
private readonly IPrerequisiteHelper prerequisiteHelper;
private static string LicensesPath => "pack://application:,,,/Assets/licenses.json"; private static string LicensesPath => "pack://application:,,,/Assets/licenses.json";
public TextToFlowDocumentConverter? TextToFlowDocumentConverter { get; set; } public TextToFlowDocumentConverter? TextToFlowDocumentConverter { get; set; }
@ -62,6 +63,8 @@ public partial class SettingsViewModel : ObservableObject
[ObservableProperty] private bool isFileSearchFlyoutOpen; [ObservableProperty] private bool isFileSearchFlyoutOpen;
[ObservableProperty] private double fileSearchProgress; [ObservableProperty] private double fileSearchProgress;
[ObservableProperty] private bool isPythonInstalling;
[ObservableProperty] private string? webApiHost; [ObservableProperty] private string? webApiHost;
[ObservableProperty] private string? webApiPort; [ObservableProperty] private string? webApiPort;
[ObservableProperty] private string? webApiActivePackageHost; [ObservableProperty] private string? webApiActivePackageHost;
@ -94,7 +97,8 @@ public partial class SettingsViewModel : ObservableObject
ISnackbarService snackbarService, ISnackbarService snackbarService,
ILogger<SettingsViewModel> logger, ILogger<SettingsViewModel> logger,
IPackageFactory packageFactory, IPackageFactory packageFactory,
ILiteDbContext liteDbContext) ILiteDbContext liteDbContext,
IPrerequisiteHelper prerequisiteHelper)
{ {
this.logger = logger; this.logger = logger;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
@ -104,6 +108,7 @@ public partial class SettingsViewModel : ObservableObject
this.a3WebApiManager = a3WebApiManager; this.a3WebApiManager = a3WebApiManager;
this.pyRunner = pyRunner; this.pyRunner = pyRunner;
this.liteDbContext = liteDbContext; this.liteDbContext = liteDbContext;
this.prerequisiteHelper = prerequisiteHelper;
SelectedTheme = settingsManager.Settings.Theme ?? "Dark"; SelectedTheme = settingsManager.Settings.Theme ?? "Dark";
WindowBackdropType = settingsManager.Settings.WindowBackdropType ?? WindowBackdropType.Mica; WindowBackdropType = settingsManager.Settings.WindowBackdropType ?? WindowBackdropType.Mica;
} }
@ -169,6 +174,16 @@ public partial class SettingsViewModel : ObservableObject
public AsyncRelayCommand PythonVersionCommand => new(async () => public AsyncRelayCommand PythonVersionCommand => new(async () =>
{ {
// Ensure python installed
if (!prerequisiteHelper.IsPythonInstalled)
{
IsPythonInstalling = true;
// Need 7z as well for site packages repack
await prerequisiteHelper.UnpackResourcesIfNecessary();
await prerequisiteHelper.InstallPythonIfNecessary();
IsPythonInstalling = false;
}
// Get python version // Get python version
await pyRunner.Initialize(); await pyRunner.Initialize();
var result = await pyRunner.GetVersionInfo(); var result = await pyRunner.GetVersionInfo();

Loading…
Cancel
Save