Browse Source

Merge branch 'main' into downloads

pull/109/head
Ionite 1 year ago committed by GitHub
parent
commit
b27657e1d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      CHANGELOG.md
  2. 1
      StabilityMatrix.Avalonia/App.axaml.cs
  3. 2
      StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
  4. 43
      StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs
  5. 28
      StabilityMatrix.Avalonia/ViewModels/CheckpointsPageViewModel.cs
  6. 7
      StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs
  7. 45
      StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs
  8. 4
      StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml
  9. 43
      StabilityMatrix.Avalonia/Views/SettingsPage.axaml
  10. 10
      StabilityMatrix.Core/Helper/HardwareHelper.cs
  11. 122
      StabilityMatrix.Core/Models/Packages/Fooocus.cs
  12. 18
      StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

8
CHANGELOG.md

@ -6,8 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html).
## v2.2.1
### Added
- New installable Package - [Fooocus](https://github.com/lllyasviel/Fooocus)
- Added "Select New Data Directory" button to Settings
### Fixed
- Fixed SD.Next shared folders config not working with new config format, reverted to Junctions / Symlinks
- Fixed issue where model version wouldn't be selected in the "All Versions" section of the Model Browser
- Improved Checkpoints page indexing performance
- Fixed issue where Checkpoints page may not show all checkpoints after clearing search filter
- Fixed issue where Checkpoints page may show incorrect checkpoints for the given filter after changing pages
## v2.2.0
### Added

1
StabilityMatrix.Avalonia/App.axaml.cs

@ -309,6 +309,7 @@ public sealed class App : Application
services.AddSingleton<BasePackage, ComfyUI>();
services.AddSingleton<BasePackage, VoltaML>();
services.AddSingleton<BasePackage, InvokeAI>();
services.AddSingleton<BasePackage, Fooocus>();
}
private static IServiceCollection ConfigureServices()

2
StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj

@ -8,7 +8,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<ApplicationIcon>./Assets/Icon.ico</ApplicationIcon>
<Version>2.1.1-dev.1</Version>
<Version>2.3.0-dev.1</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>

43
StabilityMatrix.Avalonia/ViewModels/CheckpointManager/CheckpointFile.cs

@ -196,44 +196,29 @@ public partial class CheckpointFile : ViewModelBase
/// </summary>
public static IEnumerable<CheckpointFile> FromDirectoryIndex(string directory, SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
// Get all files with supported extensions
var allExtensions = SupportedCheckpointExtensions
.Concat(SupportedImageExtensions)
.Concat(SupportedMetadataExtensions);
var files = allExtensions.AsParallel()
.SelectMany(pattern => Directory.EnumerateFiles(directory, $"*{pattern}", searchOption)).ToDictionary<string, string>(Path.GetFileName);
foreach (var file in files.Keys.Where(k => SupportedCheckpointExtensions.Contains(Path.GetExtension(k))))
foreach (var file in Directory.EnumerateFiles(directory, "*.*", searchOption))
{
var checkpointFile = new CheckpointFile()
if (!SupportedCheckpointExtensions.Any(ext => file.Contains(ext)))
continue;
var checkpointFile = new CheckpointFile
{
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.TryGetValue(cmInfoPath, out var jsonPath))
var jsonPath = Path.Combine(directory, $"{Path.GetFileNameWithoutExtension(file)}.cm-info.json");
if (File.Exists(jsonPath))
{
try
{
var jsonData = File.ReadAllText(jsonPath);
checkpointFile.ConnectedModel = ConnectedModelInfo.FromJson(jsonData);
}
catch (IOException e)
{
Debug.WriteLine($"Failed to parse {cmInfoPath}: {e}");
}
var json = File.ReadAllText(jsonPath);
var connectedModelInfo = ConnectedModelInfo.FromJson(json);
checkpointFile.ConnectedModel = connectedModelInfo;
}
// Check for preview image
var previewImage = SupportedImageExtensions.Select(ext => $"{fileNameWithoutExtension}.preview{ext}").FirstOrDefault(files.ContainsKey);
if (previewImage != null)
{
checkpointFile.PreviewImagePath = files[previewImage];
}
checkpointFile.PreviewImagePath = SupportedImageExtensions
.Select(ext => Path.Combine(directory,
$"{Path.GetFileNameWithoutExtension(file)}.preview{ext}")).Where(File.Exists)
.FirstOrDefault();
yield return checkpointFile;
}

28
StabilityMatrix.Avalonia/ViewModels/CheckpointsPageViewModel.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -10,6 +11,7 @@ using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using NLog;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.CheckpointManager;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
@ -95,7 +97,12 @@ public partial class CheckpointsPageViewModel : PageViewModelBase
{
if (string.IsNullOrWhiteSpace(SearchFilter))
{
DisplayedCheckpointFolders = CheckpointFolders;
DisplayedCheckpointFolders = new ObservableCollection<CheckpointFolder>(
CheckpointFolders.Select(x =>
{
x.SearchFilter = SearchFilter;
return x;
}));
return;
}
@ -106,7 +113,7 @@ public partial class CheckpointsPageViewModel : PageViewModelBase
folder.SearchFilter = SearchFilter;
}
DisplayedCheckpointFolders = new ObservableCollection<CheckpointManager.CheckpointFolder>(filteredFolders);
DisplayedCheckpointFolders = new ObservableCollection<CheckpointFolder>(filteredFolders);
}
private bool ContainsSearchFilter(CheckpointManager.CheckpointFolder folder)
@ -137,7 +144,7 @@ public partial class CheckpointsPageViewModel : PageViewModelBase
var folders = Directory.GetDirectories(modelsDirectory);
// Index all folders
var indexTasks = folders.Select(f => Task.Run(async () =>
var indexTasks = folders.Select(async f =>
{
var checkpointFolder =
new CheckpointManager.CheckpointFolder(settingsManager, downloadService, modelFinder)
@ -148,21 +155,26 @@ public partial class CheckpointsPageViewModel : PageViewModelBase
};
await checkpointFolder.IndexAsync();
return checkpointFolder;
})).ToList();
}).ToList();
await Task.WhenAll(indexTasks);
// Set new observable collection, ordered by alphabetical order
CheckpointFolders =
new ObservableCollection<CheckpointManager.CheckpointFolder>(indexTasks
new ObservableCollection<CheckpointFolder>(indexTasks
.Select(t => t.Result)
.OrderBy(f => f.Title));
if (!string.IsNullOrWhiteSpace(SearchFilter))
{
DisplayedCheckpointFolders = new ObservableCollection<CheckpointManager.CheckpointFolder>(
CheckpointFolders
.Where(x => x.CheckpointFiles.Any(y => y.FileName.Contains(SearchFilter))));
var filtered = CheckpointFolders
.Where(x => x.CheckpointFiles.Any(y => y.FileName.Contains(SearchFilter))).Select(
f =>
{
f.SearchFilter = SearchFilter;
return f;
});
DisplayedCheckpointFolders = new ObservableCollection<CheckpointFolder>(filtered);
}
else
{

7
StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs

@ -42,8 +42,11 @@ public partial class SelectModelVersionViewModel : ContentDialogViewModelBase
var firstImageUrl = value?.ModelVersion?.Images?.FirstOrDefault(
img => nsfwEnabled || img.Nsfw == "None")?.Url;
Dispatcher.UIThread.InvokeAsync(async
() => await UpdateImage(firstImageUrl));
Dispatcher.UIThread.InvokeAsync(async () =>
{
SelectedFile = value?.CivitFileViewModels.FirstOrDefault();
await UpdateImage(firstImageUrl);
});
}
partial void OnSelectedFileChanged(CivitFileViewModel? value)

45
StabilityMatrix.Avalonia/ViewModels/SettingsViewModel.cs

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@ -332,6 +333,50 @@ public partial class SettingsViewModel : PageViewModelBase
"Stability Matrix has been added to the Start Menu for all users.", NotificationType.Success);
}
public async Task PickNewDataDirectory()
{
var viewModel = dialogFactory.Get<SelectDataDirectoryViewModel>();
var dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new SelectDataDirectoryDialog
{
DataContext = viewModel
}
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
// 1. For portable mode, call settings.SetPortableMode()
if (viewModel.IsPortableMode)
{
settingsManager.SetPortableMode();
}
// 2. For custom path, call settings.SetLibraryPath(path)
else
{
settingsManager.SetLibraryPath(viewModel.DataDirectory);
}
// Restart
var restartDialog = new BetterContentDialog
{
Title = "Restart required",
Content = "Stability Matrix must be restarted for the changes to take effect.",
PrimaryButtonText = "Restart",
DefaultButton = ContentDialogButton.Primary,
IsSecondaryButtonEnabled = false,
};
await restartDialog.ShowAsync();
Process.Start(Compat.AppCurrentPath);
App.Shutdown();
}
}
#endregion
#region Debug Section

4
StabilityMatrix.Avalonia/Views/Dialogs/SelectDataDirectoryDialog.axaml

@ -50,7 +50,7 @@
</Grid>
<TextBlock
Text="This is where the model checkpoints, LORAs, web UIs, settings, etc. will be installed. If you were satisfied with the previous versions, you don't need to change anything here."
Text="This is where the model checkpoints, LORAs, web UIs, settings, etc. will be installed."
TextWrapping="Wrap"
Foreground="LightGray"
FontSize="12"
@ -59,7 +59,7 @@
<CheckBox
Content="Portable Mode"
IsChecked="{Binding IsPortableMode, Mode=TwoWay}"
Margin="0,16,0,0" />
Margin="0,32,0,0" />
<ui:InfoBar
IsClosable="False"

43
StabilityMatrix.Avalonia/Views/SettingsPage.axaml

@ -6,6 +6,7 @@
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:avalonia="clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="700"
x:DataType="vm:SettingsViewModel"
x:CompileBindings="True"
@ -124,7 +125,7 @@
</Grid>
<!-- System Options -->
<Grid Grid.Row="4" Margin="0,8,0,0" RowDefinitions="auto,*">
<Grid Grid.Row="4" Margin="0,8,0,0" RowDefinitions="auto, auto, auto">
<TextBlock
FontWeight="Medium"
Text="System"
@ -166,6 +167,24 @@
</StackPanel>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
<ui:SettingsExpander Grid.Row="2"
Header="Select New Data Directory"
Description="Does not move existing data"
IconSource="MoveToFolder"
Margin="8,0">
<ui:SettingsExpander.Footer>
<Button Command="{Binding PickNewDataDirectory}">
<Grid ColumnDefinitions="Auto, Auto">
<avalonia:Icon Grid.Row="0" Value="fa-solid fa-folder-open"
Margin="0,0,8,0"
VerticalAlignment="Center" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
Text="Select Directory"/>
</Grid>
</Button>
</ui:SettingsExpander.Footer>
</ui:SettingsExpander>
</Grid>
<!-- Debug Options -->
@ -181,24 +200,24 @@
IconSource="Code"
Command="{Binding LoadDebugInfo}"
Header="Debug Options"
Margin="8, 0,8,4">
Margin="8, 0,8,0">
<ui:SettingsExpanderItem Description="Paths" IconSource="Folder"
Margin="4">
Margin="4, 0">
<SelectableTextBlock Text="{Binding DebugPaths}"
Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" />
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Description="Compat Info" IconSource="StarFilled"
Margin="4,0,4,4">
Margin="4,0">
<SelectableTextBlock Text="{Binding DebugCompatInfo}"
Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" />
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Description="GPU Info" IconSource="FullScreenMaximize"
Margin="4,0,4,4">
Margin="4,0">
<SelectableTextBlock Text="{Binding DebugGpuInfo}"
Foreground="{DynamicResource TextControlPlaceholderForeground}"
TextWrapping="WrapWithOverflow" />
@ -206,10 +225,9 @@
<ui:SettingsExpanderItem Content="Animation Scale" IconSource="Clock"
Description="Lower values = faster animations. 0x means animations are instant."
Margin="4,0,4,4">
Margin="4,0">
<ui:SettingsExpanderItem.Footer>
<ComboBox Margin="0, 8"
ItemsSource="{Binding AnimationScaleOptions}"
<ComboBox ItemsSource="{Binding AnimationScaleOptions}"
SelectedItem="{Binding SelectedAnimationScale}">
<ComboBox.ItemTemplate>
<DataTemplate>
@ -223,30 +241,27 @@
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Notification" IconSource="CommentAdd"
Margin="4,0,4,4">
Margin="4,0">
<ui:SettingsExpanderItem.Footer>
<Button
Margin="0, 8"
Command="{Binding DebugNotificationCommand}"
Content="New Notification"/>
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Content Dialog" IconSource="NewWindow"
Margin="4,0,4,4">
Margin="4,0">
<ui:SettingsExpanderItem.Footer>
<Button
Margin="0, 8"
Command="{Binding DebugContentDialogCommand}"
Content="Show Dialog"/>
</ui:SettingsExpanderItem.Footer>
</ui:SettingsExpanderItem>
<ui:SettingsExpanderItem Content="Exceptions" IconSource="Flag"
Margin="4,0,4,4">
Margin="4,0">
<ui:SettingsExpanderItem.Footer>
<Button
Margin="0, 8"
Command="{Binding DebugThrowExceptionCommand}"
Content="Unhandled Exception"/>
</ui:SettingsExpanderItem.Footer>

10
StabilityMatrix.Core/Helper/HardwareHelper.cs

@ -123,6 +123,16 @@ public static partial class HardwareHelper
{
return IterGpuInfo().Any(gpu => gpu.IsAmd);
}
// Set ROCm for default if AMD and Linux
public static bool PreferRocm() => !HardwareHelper.HasNvidiaGpu()
&& HardwareHelper.HasAmdGpu()
&& Compat.IsLinux;
// Set DirectML for default if AMD and Windows
public static bool PreferDirectML() => !HardwareHelper.HasNvidiaGpu()
&& HardwareHelper.HasAmdGpu()
&& Compat.IsWindows;
}
public enum Level

122
StabilityMatrix.Core/Models/Packages/Fooocus.cs

@ -0,0 +1,122 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
public class Fooocus : BaseGitPackage
{
public Fooocus(IGithubApiCache githubApi, ISettingsManager settingsManager,
IDownloadService downloadService, IPrerequisiteHelper prerequisiteHelper) : base(githubApi,
settingsManager, downloadService, prerequisiteHelper)
{
}
public override string Name => "Fooocus";
public override string DisplayName { get; set; } = "Fooocus";
public override string Author => "lllyasviel";
public override string Blurb =>
"Fooocus is a rethinking of Stable Diffusion and Midjourney’s designs";
public override string LicenseType => "GPL-3.0";
public override string LicenseUrl => "https://github.com/lllyasviel/Fooocus/blob/main/LICENSE";
public override string LaunchCommand => "launch.py";
public override Uri PreviewImageUri =>
new("https://user-images.githubusercontent.com/19834515/261830306-f79c5981-cf80-4ee3-b06b-3fef3f8bfbc7.png");
public override List<LaunchOptionDefinition> LaunchOptions => new()
{
LaunchOptionDefinition.Extras
};
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders => new()
{
[SharedFolderType.StableDiffusion] = new[] {"models/checkpoints"},
[SharedFolderType.Diffusers] = new[] {"models/diffusers"},
[SharedFolderType.Lora] = new[] {"models/loras"},
[SharedFolderType.CLIP] = new[] {"models/clip"},
[SharedFolderType.TextualInversion] = new[] {"models/embeddings"},
[SharedFolderType.VAE] = new[] {"models/vae"},
[SharedFolderType.ApproxVAE] = new[] {"models/vae_approx"},
[SharedFolderType.ControlNet] = new[] {"models/controlnet"},
[SharedFolderType.GLIGEN] = new[] {"models/gligen"},
[SharedFolderType.ESRGAN] = new[] {"models/upscale_models"},
[SharedFolderType.Hypernetwork] = new[] {"models/hypernetworks"}
};
public override async Task<string> GetLatestVersion()
{
var release = await GetLatestRelease().ConfigureAwait(false);
return release.TagName!;
}
public override async Task InstallPackage(IProgress<ProgressReport>? progress = null)
{
await base.InstallPackage(progress).ConfigureAwait(false);
var venvRunner = await SetupVenv(InstallLocation).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing torch...", isIndeterminate: true));
var torchVersion = "cpu";
var gpus = HardwareHelper.IterGpuInfo().ToList();
if (gpus.Any(g => g.IsNvidia))
{
torchVersion = "cu118";
}
else if (HardwareHelper.PreferRocm())
{
torchVersion = "rocm5.4.2";
}
await venvRunner
.PipInstall(
$"torch==2.0.1 torchvision==0.15.2 --extra-index-url https://download.pytorch.org/whl/{torchVersion}",
OnConsoleOutput).ConfigureAwait(false);
progress?.Report(new ProgressReport(-1f, "Installing requirements...",
isIndeterminate: true));
await venvRunner.PipInstall("-r requirements_versions.txt", OnConsoleOutput)
.ConfigureAwait(false);
}
public override async Task RunPackage(string installedPackagePath, string command, string arguments)
{
await SetupVenv(installedPackagePath).ConfigureAwait(false);
void HandleConsoleOutput(ProcessOutput s)
{
OnConsoleOutput(s);
if (s.Text.Contains("To see the GUI go to", StringComparison.OrdinalIgnoreCase))
{
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)");
var match = regex.Match(s.Text);
if (match.Success)
{
WebUrl = match.Value;
}
OnStartupComplete(WebUrl);
}
}
void HandleExit(int i)
{
Debug.WriteLine($"Venv process exited with code {i}");
OnExit(i);
}
var args = $"\"{Path.Combine(installedPackagePath, command)}\" {arguments}";
VenvRunner?.RunDetached(
args.TrimEnd(),
HandleConsoleOutput,
HandleExit);
}
}

18
StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

@ -98,7 +98,7 @@ public class VladAutomatic : BaseGitPackage
{
Name = "Use DirectML if no compatible GPU is detected",
Type = LaunchOptionType.Bool,
InitialValue = PreferDirectML(),
InitialValue = HardwareHelper.PreferDirectML(),
Options = new() { "--use-directml" }
},
new()
@ -112,7 +112,7 @@ public class VladAutomatic : BaseGitPackage
{
Name = "Force use of AMD ROCm backend",
Type = LaunchOptionType.Bool,
InitialValue = PreferRocm(),
InitialValue = HardwareHelper.PreferRocm(),
Options = new() { "--use-rocm" }
},
new()
@ -137,16 +137,6 @@ public class VladAutomatic : BaseGitPackage
};
public override string ExtraLaunchArguments => "";
// Set ROCm for default if AMD and Linux
private static bool PreferRocm() => !HardwareHelper.HasNvidiaGpu()
&& HardwareHelper.HasAmdGpu()
&& Compat.IsLinux;
// Set DirectML for default if AMD and Windows
private static bool PreferDirectML() => !HardwareHelper.HasNvidiaGpu()
&& HardwareHelper.HasAmdGpu()
&& Compat.IsWindows;
public override Task<string> GetLatestVersion() => Task.FromResult("master");
@ -177,13 +167,13 @@ public class VladAutomatic : BaseGitPackage
await venvRunner.CustomInstall("launch.py --use-cuda --debug --test", OnConsoleOutput)
.ConfigureAwait(false);
}
else if (PreferRocm())
else if (HardwareHelper.PreferRocm())
{
// ROCm
await venvRunner.CustomInstall("launch.py --use-rocm --debug --test", OnConsoleOutput)
.ConfigureAwait(false);
}
else if (PreferDirectML())
else if (HardwareHelper.PreferDirectML())
{
// DirectML
await venvRunner.CustomInstall("launch.py --use-directml --debug --test", OnConsoleOutput)

Loading…
Cancel
Save