Browse Source

Merge pull request #24 from ionite34/more-restructuring

Moved a lot of the A1111 specific stuff into the A3WebUI
pull/5/head
JT 2 years ago committed by GitHub
parent
commit
f435942478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      StabilityMatrix/Helper/PackageFactory.cs
  2. 1
      StabilityMatrix/Helper/ProcessRunner.cs
  3. 2
      StabilityMatrix/Models/Api/GithubRelease.cs
  4. 18
      StabilityMatrix/Models/BasePackage.cs
  5. 10
      StabilityMatrix/Models/InstalledPackage.cs
  6. 53
      StabilityMatrix/Models/Packages/A3WebUI.cs
  7. 12
      StabilityMatrix/Models/Packages/DankDiffusion.cs
  8. 2
      StabilityMatrix/Models/Settings.cs
  9. 5
      StabilityMatrix/PyIOStream.cs
  10. 10
      StabilityMatrix/PyVenvRunner.cs
  11. 3
      StabilityMatrix/StabilityMatrix.csproj
  12. 33
      StabilityMatrix/ViewModels/InstallerViewModel.cs
  13. 91
      StabilityMatrix/ViewModels/LaunchViewModel.cs
  14. 10
      StabilityMatrix/ViewModels/SettingsViewModel.cs

26
StabilityMatrix/Helper/PackageFactory.cs

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using StabilityMatrix.Models;
using StabilityMatrix.Models.Packages;
namespace StabilityMatrix.Helper;
public static class PackageFactory
{
private static readonly List<BasePackage> AllPackages = new()
{
new A3WebUI(),
new DankDiffusion()
};
public static IEnumerable<BasePackage> GetAllAvailablePackages()
{
return AllPackages;
}
public static BasePackage? FindPackageByName(string packageName)
{
return AllPackages.FirstOrDefault(x => x.Name.Equals(packageName, StringComparison.OrdinalIgnoreCase));
}
}

1
StabilityMatrix/Helper/ProcessRunner.cs

@ -42,7 +42,6 @@ public static class ProcessRunner
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.CreateNoWindow = true;
if (environmentVariables != null)

2
StabilityMatrix/Models/Api/GithubRelease.cs

@ -5,5 +5,5 @@ namespace StabilityMatrix.Models.Api;
public class GithubRelease
{
[JsonPropertyName("tag_name")]
public string TagName { get; set; }
public string? TagName { get; set; }
}

18
StabilityMatrix/Models/BasePackage.cs

@ -10,23 +10,33 @@ public abstract class BasePackage
public abstract string Author { get; }
public abstract string GithubUrl { get; }
public abstract string LaunchCommand { get; }
public abstract string DefaultLaunchArguments { get; }
public abstract Task DownloadPackage();
public abstract Task InstallPackage();
public abstract Task RunPackage(string installedPackagePath, string arguments);
public abstract Task Shutdown();
internal virtual string DownloadLocation => $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\StabilityMatrix\\Packages\\{Name}.zip";
internal virtual string InstallLocation => $"{Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)}\\StabilityMatrix\\Packages\\{Name}";
public event EventHandler<int> DownloadProgressChanged;
public event EventHandler<string> DownloadComplete;
public event EventHandler<int> InstallProgressChanged;
public event EventHandler<string> InstallComplete;
public event EventHandler<int>? DownloadProgressChanged;
public event EventHandler<string>? DownloadComplete;
public event EventHandler<int>? InstallProgressChanged;
public event EventHandler<string>? InstallComplete;
public event EventHandler<string>? ConsoleOutput;
public event EventHandler<int>? Exited;
public event EventHandler? StartupComplete;
public void OnDownloadProgressChanged(int progress) => DownloadProgressChanged?.Invoke(this, progress);
public void OnDownloadComplete(string path) => DownloadComplete?.Invoke(this, path);
public void OnInstallProgressChanged(int progress) => InstallProgressChanged?.Invoke(this, progress);
public void OnInstallComplete(string path) => InstallComplete?.Invoke(this, path);
public void OnConsoleOutput(string output) => ConsoleOutput?.Invoke(this, output);
public void OnExit(int exitCode) => Exited?.Invoke(this, exitCode);
public void OnStartupComplete() => StartupComplete?.Invoke(this, EventArgs.Empty);
public string ByAuthor => $"By {Author}";
}

10
StabilityMatrix/Models/InstalledPackage.cs

@ -10,14 +10,14 @@ public class InstalledPackage
// Unique ID for the installation
public Guid Id { get; set; }
// User defined name
public string Name { get; set; }
public string? Name { get; set; }
// Package name
public string PackageName { get; set; }
public string? PackageName { get; set; }
// Package version
public string PackageVersion { get; set; }
public string? PackageVersion { get; set; }
// Install path
public string Path { get; set; }
public string? Path { get; set; }
public string LaunchCommand { get; set; }
public string? LaunchCommand { get; set; }
}

53
StabilityMatrix/Models/Packages/A3WebUI.cs

@ -6,6 +6,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using Refit;
using StabilityMatrix.Api;
using StabilityMatrix.Helper;
@ -14,11 +15,17 @@ namespace StabilityMatrix.Models.Packages;
public class A3WebUI : BasePackage
{
private PyVenvRunner? venvRunner;
public override string Name => "stable-diffusion-webui";
public override string DisplayName => "Stable Diffusion WebUI";
public override string Author => "AUTOMATIC1111";
public override string GithubUrl => "https://github.com/AUTOMATIC1111/stable-diffusion-webui";
public override string LaunchCommand => "launch.py";
public override string DefaultLaunchArguments => $"{GetVramOption()} {GetXformersOption()}";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public override async Task DownloadPackage()
{
@ -42,7 +49,7 @@ public class A3WebUI : BasePackage
{
response = await client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead);
contentLength = response.Content.Headers.ContentLength ?? 0;
Debug.WriteLine("Retrying...");
Logger.Debug("Retrying get-headers for content-length");
Thread.Sleep(50);
}
@ -66,7 +73,7 @@ public class A3WebUI : BasePackage
else
{
var progress = (int)(totalBytesRead * 100d / contentLength);
Debug.WriteLine($"Progress; {progress}");
Logger.Debug($"Progress; {progress}");
OnDownloadProgressChanged(progress);
}
}
@ -82,7 +89,47 @@ public class A3WebUI : BasePackage
return Task.CompletedTask;
}
public string CommandLineArgs => $"{GetVramOption()} {GetXformersOption()}";
public override async Task RunPackage(string installedPackagePath, string arguments)
{
var venvPath = Path.Combine(installedPackagePath, "venv");
// Setup venv
venvRunner?.Dispose();
venvRunner = new PyVenvRunner(venvPath);
if (!venvRunner.Exists())
{
await venvRunner.Setup();
}
void HandleConsoleOutput(string? s)
{
if (s == null) return;
if (s.Contains("model loaded", StringComparison.OrdinalIgnoreCase))
{
OnStartupComplete();
}
Debug.WriteLine($"process stdout: {s}");
OnConsoleOutput($"{s}\n");
}
void HandleExit(int i)
{
Debug.WriteLine($"Venv process exited with code {i}");
OnConsoleOutput($"Venv process exited with code {i}");
}
var args = $"\"{Path.Combine(installedPackagePath, LaunchCommand)}\"";
venvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, HandleExit);
}
public override Task Shutdown()
{
venvRunner?.Dispose();
venvRunner?.Process?.WaitForExitAsync();
return Task.CompletedTask;
}
private void UnzipPackage()
{

12
StabilityMatrix/Models/Packages/DankDiffusion.cs

@ -19,4 +19,16 @@ public class DankDiffusion : BasePackage
{
throw new System.NotImplementedException();
}
public override Task RunPackage(string installedPackagePath, string arguments)
{
throw new System.NotImplementedException();
}
public override Task Shutdown()
{
throw new System.NotImplementedException();
}
public override string DefaultLaunchArguments => "";
}

2
StabilityMatrix/Models/Settings.cs

@ -5,7 +5,7 @@ namespace StabilityMatrix.Models;
public class Settings
{
public string Theme { get; set; }
public string? Theme { get; set; }
public List<InstalledPackage> InstalledPackages { get; set; } = new();
public Guid? ActiveInstalledPackage { get; set; }
public bool HasInstalledPip { get; set; }

5
StabilityMatrix/PyIOStream.cs

@ -12,16 +12,17 @@ namespace StabilityMatrix;
[SuppressMessage("ReSharper", "InconsistentNaming")]
internal class PyIOStream
{
public event EventHandler<string> OnWriteUpdate;
private readonly StringBuilder TextBuilder;
private readonly StringWriter TextWriter;
public PyIOStream(StringBuilder builder = null)
public PyIOStream(StringBuilder? builder = null)
{
TextBuilder = builder ?? new StringBuilder();
TextWriter = new StringWriter(TextBuilder);
}
public event EventHandler<string>? OnWriteUpdate;
public void ClearBuffer()
{
TextBuilder.Clear();

10
StabilityMatrix/PyVenvRunner.cs

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using NLog;
using NLog.Fluent;
using StabilityMatrix.Helper;
namespace StabilityMatrix;
@ -27,6 +29,8 @@ public class PyVenvRunner : IDisposable
/// </summary>
public string PythonPath => RootPath + @"\Scripts\python.exe";
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public PyVenvRunner(string path)
{
RootPath = path;
@ -65,13 +69,15 @@ public class PyVenvRunner : IDisposable
}
}
public void RunDetached(string arguments, Action<string>? outputDataReceived, Action<int>? onExit = null, bool unbuffered = true)
public void RunDetached(string arguments, Action<string?>? outputDataReceived, Action<int>? onExit = null, bool unbuffered = true)
{
if (!Exists())
{
throw new InvalidOperationException("Venv python process does not exist");
}
Debug.WriteLine($"Launching RunDetached at {PythonPath} with args {arguments}");
Logger.Debug($"Launching RunDetached at {PythonPath} with args {arguments}");
if (unbuffered)
{
var env = new Dictionary<string, string>

3
StabilityMatrix/StabilityMatrix.csproj

@ -5,11 +5,14 @@
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<SelfContained>true</SelfContained>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="NLog" Version="5.1.4" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.3" />

33
StabilityMatrix/ViewModels/InstallerViewModel.cs

@ -5,9 +5,6 @@ using StabilityMatrix.Helper;
using StabilityMatrix.Models;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using StabilityMatrix.Models.Packages;
using System.Linq;
using System.Windows;
@ -40,11 +37,7 @@ public partial class InstallerViewModel : ObservableObject
this.settingsManager = settingsManager;
InstalledText = "shrug";
ProgressValue = 0;
Packages = new ObservableCollection<BasePackage>
{
new A3WebUI(),
new DankDiffusion()
};
Packages = new ObservableCollection<BasePackage>(PackageFactory.GetAllAvailablePackages());
SelectedPackage = Packages[0];
}
@ -146,24 +139,28 @@ public partial class InstallerViewModel : ObservableObject
private async Task<bool> InstallGitIfNecessary()
{
var gitOutput = await ProcessRunner.GetProcessOutputAsync("git", "--version");
if (gitOutput.Contains("git version 2"))
try
{
var gitOutput = await ProcessRunner.GetProcessOutputAsync("git", "--version");
if (gitOutput.Contains("git version 2"))
{
return true;
}
}
catch (Exception e)
{
return true;
logger.LogError(e, "Error running git: ");
}
IsIndeterminate = true;
InstalledText = "Installing Git...";
using var installProcess = ProcessRunner.StartProcess("Assets\\Git-2.40.1-64-bit.exe", "/VERYSILENT /NORESTART");
installProcess.OutputDataReceived += (sender, args) =>
{
Debug.Write(args.Data);
};
using var installProcess =
ProcessRunner.StartProcess("Assets\\Git-2.40.1-64-bit.exe", "/VERYSILENT /NORESTART");
installProcess.OutputDataReceived += (sender, args) => { Debug.Write(args.Data); };
await installProcess.WaitForExitAsync();
IsIndeterminate = false;
return installProcess.ExitCode == 0;
}
public event PropertyChangedEventHandler PropertyChanged;
}
}

91
StabilityMatrix/ViewModels/LaunchViewModel.cs

@ -21,7 +21,7 @@ namespace StabilityMatrix.ViewModels;
public partial class LaunchViewModel : ObservableObject
{
private readonly ISettingsManager settingsManager;
private PyVenvRunner? venvRunner;
private BasePackage? runningPackage;
private bool clearingPackages = false;
[ObservableProperty]
@ -58,7 +58,7 @@ public partial class LaunchViewModel : ObservableObject
[ObservableProperty]
private ObservableCollection<InstalledPackage> installedPackages = new();
public event EventHandler ScrollNeeded;
public event EventHandler? ScrollNeeded;
public LaunchViewModel(ISettingsManager settingsManager)
{
@ -80,45 +80,31 @@ public partial class LaunchViewModel : ObservableObject
await PyRunner.Initialize();
// Get path from package
var packagePath = SelectedPackage.Path;
var venvPath = Path.Combine(packagePath, "venv");
// Setup venv
venvRunner?.Dispose();
venvRunner = new PyVenvRunner(venvPath);
if (!venvRunner.Exists())
{
await venvRunner.Setup();
}
void OnConsoleOutput(string? s)
{
if (s == null) return;
Dispatcher.CurrentDispatcher.Invoke(() =>
{
Debug.WriteLine($"process stdout: {s}");
ConsoleOutput += s + "\n";
ScrollNeeded?.Invoke(this, EventArgs.Empty);
});
}
void OnExit(int i)
var packagePath = SelectedPackage.Path!;
var basePackage = PackageFactory.FindPackageByName(SelectedPackage.Name!);
if (basePackage == null)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
Debug.WriteLine($"Venv process exited with code {i}");
ConsoleOutput += $"Venv process exited with code {i}";
ScrollNeeded?.Invoke(this, EventArgs.Empty);
SetProcessRunning(false);
});
throw new InvalidOperationException("Package not found");
}
var args = "\"" + Path.Combine(packagePath, SelectedPackage.LaunchCommand) + "\"";
venvRunner.RunDetached(args, OnConsoleOutput, OnExit);
basePackage.ConsoleOutput += OnConsoleOutput;
basePackage.Exited += OnExit;
basePackage.StartupComplete += RunningPackageOnStartupComplete;
await basePackage.RunPackage(packagePath, string.Empty);
runningPackage = basePackage;
SetProcessRunning(true);
});
private void RunningPackageOnStartupComplete(object? sender, EventArgs e)
{
Process.Start(new ProcessStartInfo
{
FileName = "http://localhost:7860",
UseShellExecute = true
});
}
public void OnLoaded()
{
LoadPackages();
@ -137,20 +123,23 @@ public partial class LaunchViewModel : ObservableObject
public void OnShutdown()
{
venvRunner?.Dispose();
Stop();
}
[RelayCommand]
private async Task Stop()
private Task Stop()
{
venvRunner?.Dispose();
if (venvRunner?.Process != null)
if (runningPackage != null)
{
await venvRunner.Process.WaitForExitAsync();
runningPackage.StartupComplete -= RunningPackageOnStartupComplete;
runningPackage.ConsoleOutput -= OnConsoleOutput;
runningPackage.Exited -= OnExit;
}
venvRunner = null;
runningPackage?.Shutdown();
runningPackage = null;
SetProcessRunning(false);
ConsoleOutput += $"{Environment.NewLine}Stopped process at {DateTimeOffset.Now}{Environment.NewLine}";
return Task.CompletedTask;
}
private void LoadPackages()
@ -184,4 +173,24 @@ public partial class LaunchViewModel : ObservableObject
StopButtonVisibility = Visibility.Collapsed;
}
}
private void OnConsoleOutput(object? sender, string output)
{
if (output == null) return;
Dispatcher.CurrentDispatcher.Invoke(() =>
{
ConsoleOutput += output + "\n";
ScrollNeeded?.Invoke(this, EventArgs.Empty);
});
}
private void OnExit(object? sender, int exitCode)
{
Dispatcher.CurrentDispatcher.Invoke(() =>
{
ConsoleOutput += $"Venv process exited with code {exitCode}";
ScrollNeeded?.Invoke(this, EventArgs.Empty);
SetProcessRunning(false);
});
}
}

10
StabilityMatrix/ViewModels/SettingsViewModel.cs

@ -29,11 +29,11 @@ public partial class SettingsViewModel : ObservableObject
{
this.settingsManager = settingsManager;
this.contentDialogService = contentDialogService;
SelectedTheme = settingsManager.Settings.Theme;
SelectedTheme = settingsManager.Settings.Theme ?? "Dark";
}
[ObservableProperty] private string selectedTheme;
[ObservableProperty]
private string selectedTheme;
partial void OnSelectedThemeChanged(string value)
{
@ -56,9 +56,9 @@ public partial class SettingsViewModel : ObservableObject
private string gpuInfo =
$"{HardwareHelper.GetGpuChipName()} ({HardwareHelper.GetGpuMemoryBytes() / 1024 / 1024 / 1024} GB)";
[ObservableProperty] private string gitInfo;
[ObservableProperty] private string? gitInfo;
[ObservableProperty] private string testProperty;
[ObservableProperty] private string? testProperty;
public AsyncRelayCommand PythonVersionCommand => new(async () =>
{

Loading…
Cancel
Save