Browse Source

implemented package updater

- Packages are now injected & so is IPackageFactory
- Updater checks on load, so probably only once per launch? but should be fine
- Moved install button
- Multi-purpose install button
- probably more idk
pull/5/head
JT 1 year ago
parent
commit
8ae6d1f483
  1. 6
      StabilityMatrix/App.xaml.cs
  2. 2
      StabilityMatrix/Helper/EventManager.cs
  3. 10
      StabilityMatrix/Helper/IPackageFactory.cs
  4. 1
      StabilityMatrix/Helper/ISettingsManager.cs
  5. 22
      StabilityMatrix/Helper/PackageFactory.cs
  6. 13
      StabilityMatrix/Helper/SettingsManager.cs
  7. 23
      StabilityMatrix/InstallPage.xaml
  8. 9
      StabilityMatrix/MainWindow.xaml.cs
  9. 15
      StabilityMatrix/Models/BasePackage.cs
  10. 1
      StabilityMatrix/Models/InstalledPackage.cs
  11. 92
      StabilityMatrix/Models/Packages/A3WebUI.cs
  12. 14
      StabilityMatrix/Models/Packages/DankDiffusion.cs
  13. 106
      StabilityMatrix/ViewModels/InstallerViewModel.cs
  14. 6
      StabilityMatrix/ViewModels/LaunchViewModel.cs
  15. 7
      StabilityMatrix/ViewModels/MainWindowViewModel.cs

6
StabilityMatrix/App.xaml.cs

@ -5,12 +5,15 @@ using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows;
using Windows.ApplicationModel;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;
using Refit;
using StabilityMatrix.Api;
using StabilityMatrix.Helper;
using StabilityMatrix.Models;
using StabilityMatrix.Models.Packages;
using StabilityMatrix.Services;
using StabilityMatrix.ViewModels;
using Wpf.Ui.Contracts;
@ -46,6 +49,9 @@ namespace StabilityMatrix
serviceCollection.AddSingleton<InstallerViewModel>();
serviceCollection.AddSingleton<TextToImageViewModel>();
serviceCollection.AddSingleton<IContentDialogService, ContentDialogService>();
serviceCollection.AddSingleton<IPackageFactory, PackageFactory>();
serviceCollection.AddSingleton<BasePackage, A3WebUI>();
serviceCollection.AddSingleton<BasePackage, DankDiffusion>();
serviceCollection.AddSingleton<ISnackbarService, SnackbarService>();
serviceCollection.AddSingleton<ISettingsManager, SettingsManager>();

2
StabilityMatrix/Helper/EventManager.cs

@ -12,5 +12,7 @@ public class EventManager
}
public event EventHandler<int>? GlobalProgressChanged;
public event EventHandler<Type>? PageChangeRequested;
public void OnGlobalProgressChanged(int progress) => GlobalProgressChanged?.Invoke(this, progress);
public void RequestPageChange(Type pageType) => PageChangeRequested?.Invoke(this, pageType);
}

10
StabilityMatrix/Helper/IPackageFactory.cs

@ -0,0 +1,10 @@
using System.Collections.Generic;
using StabilityMatrix.Models;
namespace StabilityMatrix.Helper;
public interface IPackageFactory
{
IEnumerable<BasePackage> GetAllAvailablePackages();
BasePackage? FindPackageByName(string packageName);
}

1
StabilityMatrix/Helper/ISettingsManager.cs

@ -11,4 +11,5 @@ public interface ISettingsManager
void SetHasInstalledPip(bool hasInstalledPip);
void SetHasInstalledVenv(bool hasInstalledVenv);
void SetNavExpanded(bool navExpanded);
void UpdatePackageVersionNumber(string packageName, string newVersion);
}

22
StabilityMatrix/Helper/PackageFactory.cs

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

13
StabilityMatrix/Helper/SettingsManager.cs

@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using StabilityMatrix.Models;
@ -74,6 +75,18 @@ public class SettingsManager : ISettingsManager
SaveSettings();
}
public void UpdatePackageVersionNumber(string packageName, string? newVersion)
{
var package = Settings.InstalledPackages.FirstOrDefault(x => x.PackageName == packageName);
if (package == null || newVersion == null)
{
return;
}
package.PackageVersion = newVersion;
SaveSettings();
}
private void LoadSettings()
{
var settingsContent = File.ReadAllText(SettingsPath);

23
StabilityMatrix/InstallPage.xaml

@ -19,17 +19,18 @@
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Vertical">
<Button Content="Install" Command="{Binding InstallCommand}" Width="100" Height="50"
HorizontalAlignment="Center" />
<TextBlock Text="{Binding InstalledText}" HorizontalAlignment="Center" Padding="8" />
<ProgressBar Visibility="{Binding ProgressBarVisibility, FallbackValue=Visible}"
Value="{Binding ProgressValue, FallbackValue=10}"
<StackPanel Grid.Row="0" Orientation="Vertical"
Visibility="{Binding ProgressBarVisibility, FallbackValue=Visible}">
<ProgressBar Value="{Binding ProgressValue, FallbackValue=10}"
IsIndeterminate="{Binding IsIndeterminate, FallbackValue=False}"
Maximum="100" Width="500" />
<TextBlock Text="{Binding ProgressText, FallbackValue=Installing...}"
HorizontalAlignment="Center" Padding="8" />
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="16" HorizontalAlignment="Left" Height="400">
<StackPanel Grid.Row="1" Orientation="Horizontal"
Margin="16" HorizontalAlignment="Left">
<ListView ItemsSource="{Binding Packages}"
SelectedItem="{Binding SelectedPackage, Mode=TwoWay}">
@ -67,6 +68,14 @@
<Run Text="{Binding SelectedPackage.GithubUrl, Mode=OneWay}" TextDecorations="Underline" />
</TextBlock>
</ui:Hyperlink>
<Button Content="{Binding InstallButtonText, FallbackValue=Install}"
Command="{Binding InstallCommand}"
IsEnabled="{Binding InstallButtonEnabled, FallbackValue=true}"
Width="100" Height="50"
Background="Green"
Margin="0,16,0,0"
VerticalAlignment="Top" />
</StackPanel>
</StackPanel>
</Grid>

9
StabilityMatrix/MainWindow.xaml.cs

@ -1,3 +1,4 @@
using System;
using System.Diagnostics;
using System.Windows;
using StabilityMatrix.Helper;
@ -5,6 +6,7 @@ using StabilityMatrix.ViewModels;
using Wpf.Ui.Contracts;
using Wpf.Ui.Controls.Navigation;
using Wpf.Ui.Controls.Window;
using EventManager = StabilityMatrix.Helper.EventManager;
namespace StabilityMatrix
{
@ -30,6 +32,13 @@ namespace StabilityMatrix
RootNavigation.SetPageService(pageService);
contentDialogService.SetContentPresenter(RootContentDialog);
EventManager.Instance.PageChangeRequested += InstanceOnPageChangeRequested;
}
private void InstanceOnPageChangeRequested(object? sender, Type e)
{
RootNavigation.Navigate(e);
}
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)

15
StabilityMatrix/Models/BasePackage.cs

@ -11,13 +11,13 @@ public abstract class BasePackage
public abstract string GithubUrl { get; }
public abstract string LaunchCommand { get; }
public abstract string DefaultLaunchArguments { get; }
public abstract Task DownloadPackage();
public abstract Task InstallPackage();
public virtual bool UpdateAvailable { get; set; }
public abstract Task<string?> DownloadPackage(bool isUpdate = false);
public abstract Task InstallPackage(bool isUpdate = false);
public abstract Task RunPackage(string installedPackagePath, string arguments);
public abstract Task Shutdown();
public abstract Task<bool> CheckForUpdates();
public abstract Task<string?> Update();
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}";
@ -26,6 +26,8 @@ public abstract class BasePackage
public event EventHandler<string>? DownloadComplete;
public event EventHandler<int>? InstallProgressChanged;
public event EventHandler<string>? InstallComplete;
public event EventHandler<int>? UpdateProgressChanged;
public event EventHandler<string>? UpdateComplete;
public event EventHandler<string>? ConsoleOutput;
public event EventHandler<int>? Exited;
public event EventHandler<string>? StartupComplete;
@ -37,6 +39,9 @@ public abstract class BasePackage
public void OnConsoleOutput(string output) => ConsoleOutput?.Invoke(this, output);
public void OnExit(int exitCode) => Exited?.Invoke(this, exitCode);
public void OnStartupComplete(string url) => StartupComplete?.Invoke(this, url);
public void OnUpdateProgressChanged(int progress) => UpdateProgressChanged?.Invoke(this, progress);
public void OnUpdateComplete(string path) => UpdateComplete?.Invoke(this, path);
public string ByAuthor => $"By {Author}";
}

1
StabilityMatrix/Models/InstalledPackage.cs

@ -19,5 +19,4 @@ public class InstalledPackage
public string? Path { get; set; }
public string? LaunchCommand { get; set; }
}

92
StabilityMatrix/Models/Packages/A3WebUI.cs

@ -2,6 +2,7 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
@ -15,6 +16,7 @@ namespace StabilityMatrix.Models.Packages;
public class A3WebUI : BasePackage
{
private readonly ISettingsManager settingsManager;
private PyVenvRunner? venvRunner;
public override string Name => "stable-diffusion-webui";
@ -24,11 +26,18 @@ public class A3WebUI : BasePackage
public override string LaunchCommand => "launch.py";
public override string DefaultLaunchArguments => $"{GetVramOption()} {GetXformersOption()}";
public override bool UpdateAvailable { get; set; } = false;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
private string webUrl = string.Empty;
public override async Task DownloadPackage()
public A3WebUI(ISettingsManager settingsManager)
{
this.settingsManager = settingsManager;
}
public override async Task<string?> DownloadPackage(bool isUpdate = false)
{
var githubApi = RestService.For<IGithubApi>("https://api.github.com");
var latestRelease = await githubApi.GetLatestRelease("AUTOMATIC1111", "stable-diffusion-webui");
@ -69,24 +78,50 @@ public class A3WebUI : BasePackage
if (isIndeterminate)
{
OnDownloadProgressChanged(-1);
if (isUpdate)
{
OnUpdateProgressChanged(-1);
}
else
{
OnDownloadProgressChanged(-1);
}
}
else
{
var progress = (int)(totalBytesRead * 100d / contentLength);
Logger.Debug($"Progress; {progress}");
OnDownloadProgressChanged(progress);
if (isUpdate)
{
OnUpdateProgressChanged(progress);
}
else
{
OnDownloadProgressChanged(progress);
}
}
}
await file.FlushAsync();
OnDownloadComplete(DownloadLocation);
return latestRelease.TagName;
}
public override Task InstallPackage()
public override Task InstallPackage(bool isUpdate = false)
{
UnzipPackage();
OnInstallComplete("Installation complete");
UnzipPackage(isUpdate);
if (isUpdate)
{
OnUpdateComplete("Update complete");
}
else
{
OnInstallComplete("Installation complete");
}
return Task.CompletedTask;
}
@ -135,10 +170,40 @@ public class A3WebUI : BasePackage
return Task.CompletedTask;
}
public override async Task<bool> CheckForUpdates()
{
var currentVersion = settingsManager.Settings.InstalledPackages.FirstOrDefault(x => x.Name == Name)
?.PackageVersion;
if (string.IsNullOrWhiteSpace(currentVersion))
{
return false;
}
var githubApi = RestService.For<IGithubApi>("https://api.github.com");
var latestRelease = await githubApi.GetLatestRelease("AUTOMATIC1111", "stable-diffusion-webui");
UpdateAvailable = latestRelease.TagName != currentVersion;
return latestRelease.TagName != currentVersion;
}
public override async Task<string?> Update()
{
var version = await DownloadPackage(true);
await InstallPackage(true);
private void UnzipPackage()
return version;
}
private void UnzipPackage(bool isUpdate = false)
{
OnInstallProgressChanged(0);
if (isUpdate)
{
OnInstallProgressChanged(0);
}
else
{
OnUpdateProgressChanged(0);
}
Directory.CreateDirectory(InstallLocation);
@ -170,9 +235,16 @@ public class A3WebUI : BasePackage
currentEntry++;
var progressValue = (int)((double)currentEntry / totalEntries * 100);
OnInstallProgressChanged(progressValue);
}
if (isUpdate)
{
OnUpdateProgressChanged(progressValue);
}
else
{
OnInstallProgressChanged(progressValue);
}
}
}
private static string GetVramOption()

14
StabilityMatrix/Models/Packages/DankDiffusion.cs

@ -10,12 +10,12 @@ public class DankDiffusion : BasePackage
public override string GithubUrl => "https://github.com/mohnjiles/dank-diffusion";
public override string LaunchCommand => "";
public override Task DownloadPackage()
public override Task<string?> DownloadPackage(bool isUpdate = false)
{
throw new System.NotImplementedException();
}
public override Task InstallPackage()
public override Task InstallPackage(bool isUpdate = false)
{
throw new System.NotImplementedException();
}
@ -30,5 +30,15 @@ public class DankDiffusion : BasePackage
throw new System.NotImplementedException();
}
public override Task<bool> CheckForUpdates()
{
throw new System.NotImplementedException();
}
public override Task<string?> Update()
{
throw new System.NotImplementedException();
}
public override string DefaultLaunchArguments => "";
}

106
StabilityMatrix/ViewModels/InstallerViewModel.cs

@ -6,9 +6,11 @@ using StabilityMatrix.Models;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.Logging;
using Wpf.Ui.Contracts;
using EventManager = StabilityMatrix.Helper.EventManager;
namespace StabilityMatrix.ViewModels;
@ -17,32 +19,62 @@ public partial class InstallerViewModel : ObservableObject
{
private readonly ILogger<InstallerViewModel> logger;
private readonly ISettingsManager settingsManager;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ProgressBarVisibility))]
private int progressValue;
[ObservableProperty]
private BasePackage selectedPackage;
[ObservableProperty]
private string installedText;
private string progressText;
[ObservableProperty]
private bool isIndeterminate;
[ObservableProperty]
private Visibility packageInstalledVisibility;
[ObservableProperty]
private string installButtonText;
[ObservableProperty]
private bool installButtonEnabled;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(SelectedPackage))]
private bool updateAvailable;
public InstallerViewModel(ILogger<InstallerViewModel> logger, ISettingsManager settingsManager)
public InstallerViewModel(ILogger<InstallerViewModel> logger, ISettingsManager settingsManager,
IPackageFactory packageFactory)
{
this.logger = logger;
this.settingsManager = settingsManager;
InstalledText = "shrug";
ProgressText = "shrug";
InstallButtonText = "Install";
installButtonEnabled = true;
ProgressValue = 0;
Packages = new ObservableCollection<BasePackage>(PackageFactory.GetAllAvailablePackages());
Packages = new ObservableCollection<BasePackage>(packageFactory.GetAllAvailablePackages());
SelectedPackage = Packages[0];
}
public Task OnLoaded()
public async Task OnLoaded()
{
return Task.CompletedTask;
var installedPackages = settingsManager.Settings.InstalledPackages;
if (installedPackages.Count == 0)
{
return;
}
foreach (var packageToUpdate in installedPackages
.Select(package => Packages.FirstOrDefault(x => x.Name == package.Name))
.Where(packageToUpdate => packageToUpdate != null))
{
await packageToUpdate!.CheckForUpdates();
OnSelectedPackageChanged(packageToUpdate);
}
}
public ObservableCollection<BasePackage> Packages { get; }
@ -50,15 +82,32 @@ public partial class InstallerViewModel : ObservableObject
partial void OnSelectedPackageChanged(BasePackage value)
{
var installed = settingsManager.Settings.InstalledPackages;
PackageInstalledVisibility = installed.FirstOrDefault(package => package.Name == value.Name) != null
? Visibility.Visible
: Visibility.Collapsed;
var isInstalled = installed.FirstOrDefault(package => package.Name == value.Name) != null;
PackageInstalledVisibility = isInstalled ? Visibility.Visible : Visibility.Collapsed;
UpdateAvailable = value.UpdateAvailable;
InstallButtonText = value.UpdateAvailable ? "Update" : isInstalled ? "Launch" : "Install";
}
public Visibility ProgressBarVisibility => ProgressValue > 0 || IsIndeterminate ? Visibility.Visible : Visibility.Collapsed;
[RelayCommand]
private async Task Install()
{
switch (InstallButtonText.ToLower())
{
case "install":
await ActuallyInstall();
break;
case "update":
await UpdateSelectedPackage();
break;
case "launch":
EventManager.Instance.RequestPageChange(typeof(LaunchPage));
break;
}
}
private async Task ActuallyInstall()
{
var installSuccess = await InstallGitIfNecessary();
if (!installSuccess)
@ -67,10 +116,10 @@ public partial class InstallerViewModel : ObservableObject
return;
}
await DownloadPackage();
var version = await DownloadPackage();
await InstallPackage();
InstalledText = "Installing dependencies...";
ProgressText = "Installing dependencies...";
await PyRunner.Initialize();
if (!settingsManager.Settings.HasInstalledPip)
{
@ -84,7 +133,7 @@ public partial class InstallerViewModel : ObservableObject
settingsManager.SetHasInstalledVenv(true);
}
InstalledText = "Done";
ProgressText = "Done";
IsIndeterminate = false;
SelectedPackageOnProgressChanged(this, 100);
@ -98,7 +147,7 @@ public partial class InstallerViewModel : ObservableObject
Path = SelectedPackage.InstallLocation,
Id = Guid.NewGuid(),
PackageName = SelectedPackage.Name,
PackageVersion = "idklol",
PackageVersion = version,
LaunchCommand = SelectedPackage.LaunchCommand
};
settingsManager.AddInstalledPackage(package);
@ -106,20 +155,35 @@ public partial class InstallerViewModel : ObservableObject
}
}
private async Task UpdateSelectedPackage()
{
ProgressText = "Updating...";
SelectedPackageOnProgressChanged(this, 0);
SelectedPackage.UpdateProgressChanged += SelectedPackageOnProgressChanged;
SelectedPackage.UpdateComplete += (_, s) => ProgressText = s;
var version = await SelectedPackage.Update();
settingsManager.UpdatePackageVersionNumber(SelectedPackage.Name, version);
SelectedPackage.UpdateAvailable = false;
UpdateAvailable = false;
InstallButtonText = "Launch";
SelectedPackageOnProgressChanged(this, 100);
}
private async Task DownloadPackage()
private Task<string?> DownloadPackage()
{
SelectedPackage.DownloadProgressChanged += SelectedPackageOnProgressChanged;
SelectedPackage.DownloadComplete += (_, _) => InstalledText = "Download Complete";
InstalledText = "Downloading package...";
await SelectedPackage.DownloadPackage();
SelectedPackage.DownloadComplete += (_, _) => ProgressText = "Download Complete";
ProgressText = "Downloading package...";
return SelectedPackage.DownloadPackage();
}
private async Task InstallPackage()
{
SelectedPackage.InstallProgressChanged += SelectedPackageOnProgressChanged;
SelectedPackage.InstallComplete += (_, _) => InstalledText = "Install Complete";
InstalledText = "Installing package...";
SelectedPackage.InstallComplete += (_, _) => ProgressText = "Install Complete";
ProgressText = "Installing package...";
await SelectedPackage.InstallPackage();
}
@ -154,7 +218,7 @@ public partial class InstallerViewModel : ObservableObject
}
IsIndeterminate = true;
InstalledText = "Installing Git...";
ProgressText = "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); };

6
StabilityMatrix/ViewModels/LaunchViewModel.cs

@ -16,6 +16,7 @@ namespace StabilityMatrix.ViewModels;
public partial class LaunchViewModel : ObservableObject
{
private readonly ISettingsManager settingsManager;
private readonly IPackageFactory packageFactory;
private BasePackage? runningPackage;
private bool clearingPackages = false;
@ -55,9 +56,10 @@ public partial class LaunchViewModel : ObservableObject
public event EventHandler? ScrollNeeded;
public LaunchViewModel(ISettingsManager settingsManager)
public LaunchViewModel(ISettingsManager settingsManager, IPackageFactory packageFactory)
{
this.settingsManager = settingsManager;
this.packageFactory = packageFactory;
SetProcessRunning(false);
ToastNotificationManagerCompat.OnActivated += ToastNotificationManagerCompatOnOnActivated;
@ -86,7 +88,7 @@ public partial class LaunchViewModel : ObservableObject
// Get path from package
var packagePath = SelectedPackage.Path!;
var basePackage = PackageFactory.FindPackageByName(SelectedPackage.Name!);
var basePackage = packageFactory.FindPackageByName(SelectedPackage.Name!);
if (basePackage == null)
{
throw new InvalidOperationException("Package not found");

7
StabilityMatrix/ViewModels/MainWindowViewModel.cs

@ -1,13 +1,12 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Shell;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Python.Runtime;
using StabilityMatrix.Helper;
using Wpf.Ui.Appearance;
using Dispatcher = System.Windows.Threading.Dispatcher;
using EventManager = StabilityMatrix.Helper.EventManager;
namespace StabilityMatrix.ViewModels;
@ -40,6 +39,10 @@ public partial class MainWindowViewModel : ObservableObject
EventManager.Instance.GlobalProgressChanged += OnGlobalProgressChanged;
}
/// <summary>
/// Updates the taskbar progress bar value and state.
/// </summary>
/// <param name="progress">Progress value from 0 to 100</param>
private void OnGlobalProgressChanged(object? sender, int progress)
{
if (progress == -1)

Loading…
Cancel
Save