Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

448 lines
15 KiB

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Layout;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using NLog;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Database;
using StabilityMatrix.Core.Models.PackageModification;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
public partial class InstallerViewModel : ContentDialogViewModelBase
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
1 year ago
private readonly ISettingsManager settingsManager;
private readonly IPyRunner pyRunner;
private readonly IDownloadService downloadService;
private readonly INotificationService notificationService;
private readonly IPrerequisiteHelper prerequisiteHelper;
1 year ago
[ObservableProperty]
private BasePackage selectedPackage;
[ObservableProperty]
private PackageVersion? selectedVersion;
[ObservableProperty]
private IReadOnlyList<BasePackage>? availablePackages;
[ObservableProperty]
private ObservableCollection<GitCommit>? availableCommits;
[ObservableProperty]
private ObservableCollection<PackageVersion>? availableVersions;
[ObservableProperty]
private GitCommit? selectedCommit;
[ObservableProperty]
private string? releaseNotes;
[ObservableProperty]
private string latestVersionText = string.Empty;
[ObservableProperty]
private bool isAdvancedMode;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanInstall))]
1 year ago
private bool showDuplicateWarning;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanInstall))]
1 year ago
private string? installName;
[ObservableProperty]
private SharedFolderMethod selectedSharedFolderMethod;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowTorchVersionOptions))]
private TorchVersion selectedTorchVersion;
1 year ago
// Version types (release or commit)
[ObservableProperty]
1 year ago
[NotifyPropertyChangedFor(
nameof(ReleaseLabelText),
nameof(IsReleaseMode),
nameof(SelectedVersion)
)]
private PackageVersionType selectedVersionType = PackageVersionType.Commit;
1 year ago
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsReleaseModeAvailable))]
1 year ago
private PackageVersionType availableVersionTypes =
PackageVersionType.GithubRelease | PackageVersionType.Commit;
1 year ago
public string ReleaseLabelText => IsReleaseMode ? "Version" : "Branch";
public bool IsReleaseMode
{
get => SelectedVersionType == PackageVersionType.GithubRelease;
1 year ago
set =>
SelectedVersionType = value
? PackageVersionType.GithubRelease
: PackageVersionType.Commit;
}
1 year ago
public bool IsReleaseModeAvailable =>
AvailableVersionTypes.HasFlag(PackageVersionType.GithubRelease);
public bool ShowTorchVersionOptions => SelectedTorchVersion != TorchVersion.None;
1 year ago
public bool CanInstall => !string.IsNullOrWhiteSpace(InstallName) && !ShowDuplicateWarning;
public ProgressViewModel InstallProgress { get; } = new();
public IEnumerable<IPackageStep> Steps { get; set; }
public InstallerViewModel(
ISettingsManager settingsManager,
IPackageFactory packageFactory,
IPyRunner pyRunner,
1 year ago
IDownloadService downloadService,
INotificationService notificationService,
IPrerequisiteHelper prerequisiteHelper
)
{
this.settingsManager = settingsManager;
this.pyRunner = pyRunner;
this.downloadService = downloadService;
this.notificationService = notificationService;
this.prerequisiteHelper = prerequisiteHelper;
// AvailablePackages and SelectedPackage
1 year ago
AvailablePackages = new ObservableCollection<BasePackage>(
packageFactory.GetAllAvailablePackages()
);
SelectedPackage = AvailablePackages[0];
}
public override void OnLoaded()
{
1 year ago
if (AvailablePackages == null)
return;
SelectedPackage = AvailablePackages[0];
IsReleaseMode = !SelectedPackage.ShouldIgnoreReleases;
}
1 year ago
public override async Task OnLoadedAsync()
{
1 year ago
if (Design.IsDesignMode)
return;
// Check for updates
try
{
var versionOptions = await SelectedPackage.GetAllVersionOptions();
if (IsReleaseMode)
{
1 year ago
AvailableVersions = new ObservableCollection<PackageVersion>(
versionOptions.AvailableVersions
);
if (!AvailableVersions.Any())
return;
SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease);
}
else
{
1 year ago
AvailableVersions = new ObservableCollection<PackageVersion>(
versionOptions.AvailableBranches
);
UpdateSelectedVersionToLatestMain();
}
ReleaseNotes = SelectedVersion?.ReleaseNotesMarkdown;
}
catch (Exception e)
{
Logger.Warn("Error getting versions: {Exception}", e.ToString());
}
}
1 year ago
[RelayCommand]
private async Task Install()
{
1 year ago
var result = await notificationService.TryAsync(
ActuallyInstall(),
"Could not install package"
);
if (result.IsSuccessful)
{
1 year ago
OnPrimaryButtonClick();
}
else
{
var ex = result.Exception!;
Logger.Error(ex, $"Error installing package: {ex}");
var dialog = new BetterContentDialog
{
Title = "Error installing package",
Content = ex.ToString(),
CloseButtonText = "Close"
};
await dialog.ShowAsync();
}
}
1 year ago
private Task ActuallyInstall()
{
if (string.IsNullOrWhiteSpace(InstallName))
{
1 year ago
notificationService.Show(
new Notification(
"Package name is empty",
"Please enter a name for the package",
NotificationType.Error
)
);
return Task.CompletedTask;
}
1 year ago
var setPackageInstallingStep = new SetPackageInstallingStep(settingsManager, InstallName);
var installLocation = Path.Combine(settingsManager.LibraryDir, "Packages", InstallName);
var prereqStep = new SetupPrerequisitesStep(prerequisiteHelper, pyRunner);
1 year ago
var downloadOptions = new DownloadPackageVersionOptions();
var installedVersion = new InstalledPackageVersion();
if (IsReleaseMode)
{
1 year ago
downloadOptions.VersionTag =
SelectedVersion?.TagName
?? throw new NullReferenceException("Selected version is null");
installedVersion.InstalledReleaseVersion = downloadOptions.VersionTag;
}
else
{
1 year ago
downloadOptions.CommitHash =
SelectedCommit?.Sha ?? throw new NullReferenceException("Selected commit is null");
installedVersion.InstalledBranch =
SelectedVersion?.TagName
?? throw new NullReferenceException("Selected version is null");
installedVersion.InstalledCommitSha = downloadOptions.CommitHash;
}
1 year ago
var downloadStep = new DownloadPackageVersionStep(
SelectedPackage,
installLocation,
downloadOptions
);
var installStep = new InstallPackageStep(
SelectedPackage,
SelectedTorchVersion,
installLocation
);
var setupModelFoldersStep = new SetupModelFoldersStep(
SelectedPackage,
SelectedSharedFolderMethod,
installLocation
);
var package = new InstalledPackage
{
DisplayName = InstallName,
LibraryPath = Path.Combine("Packages", InstallName),
Id = Guid.NewGuid(),
PackageName = SelectedPackage.Name,
Version = installedVersion,
LaunchCommand = SelectedPackage.LaunchCommand,
LastUpdateCheck = DateTimeOffset.Now,
PreferredTorchVersion = SelectedTorchVersion,
PreferredSharedFolderMethod = SelectedSharedFolderMethod
};
var addInstalledPackageStep = new AddInstalledPackageStep(settingsManager, package);
1 year ago
var steps = new List<IPackageStep>
{
setPackageInstallingStep,
prereqStep,
downloadStep,
installStep,
setupModelFoldersStep,
addInstalledPackageStep
};
Steps = steps;
return Task.CompletedTask;
}
public void Cancel()
{
OnCloseButtonClick();
}
1 year ago
private void UpdateSelectedVersionToLatestMain()
{
if (AvailableVersions is null)
{
SelectedVersion = null;
}
else
{
// First try to find master
var version = AvailableVersions.FirstOrDefault(x => x.TagName == "master");
// If not found, try main
version ??= AvailableVersions.FirstOrDefault(x => x.TagName == "main");
1 year ago
// If still not found, just use the first one
version ??= AvailableVersions.FirstOrDefault();
1 year ago
SelectedVersion = version;
}
}
1 year ago
[RelayCommand]
private async Task ShowPreview()
{
var url = SelectedPackage.PreviewImageUri.ToString();
var imageStream = await downloadService.GetImageStreamFromUrl(url);
var bitmap = new Bitmap(imageStream);
1 year ago
var dialog = new ContentDialog
{
PrimaryButtonText = "Open in Browser",
CloseButtonText = "Close",
Content = new Image
{
1 year ago
Source = bitmap,
Stretch = Stretch.Uniform,
MaxHeight = 500,
HorizontalAlignment = HorizontalAlignment.Center
}
};
1 year ago
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
ProcessRunner.OpenUrl(url);
}
}
// When available version types change, reset selected version type if not compatible
partial void OnAvailableVersionTypesChanged(PackageVersionType value)
{
if (!value.HasFlag(SelectedVersionType))
{
SelectedVersionType = value;
}
}
1 year ago
// When changing branch / release modes, refresh
// ReSharper disable once UnusedParameterInPartialMethod
1 year ago
partial void OnSelectedVersionTypeChanged(PackageVersionType value) =>
OnSelectedPackageChanged(SelectedPackage);
partial void OnSelectedPackageChanged(BasePackage value)
{
ReleaseNotes = string.Empty;
AvailableVersions?.Clear();
AvailableCommits?.Clear();
AvailableVersionTypes = SelectedPackage.ShouldIgnoreReleases
? PackageVersionType.Commit
: PackageVersionType.GithubRelease | PackageVersionType.Commit;
SelectedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod;
SelectedTorchVersion = SelectedPackage.GetRecommendedTorchVersion();
1 year ago
if (Design.IsDesignMode)
return;
Dispatcher.UIThread
.InvokeAsync(async () =>
{
1 year ago
Logger.Debug($"Release mode: {IsReleaseMode}");
var versionOptions = await value.GetAllVersionOptions();
1 year ago
AvailableVersions = IsReleaseMode
? new ObservableCollection<PackageVersion>(versionOptions.AvailableVersions)
: new ObservableCollection<PackageVersion>(versionOptions.AvailableBranches);
SelectedVersion = AvailableVersions.First(x => !x.IsPrerelease);
ReleaseNotes = SelectedVersion.ReleaseNotesMarkdown;
Logger.Debug($"Loaded release notes for {ReleaseNotes}");
if (!IsReleaseMode)
{
var commits = (await value.GetAllCommits(SelectedVersion.TagName))?.ToList();
if (commits is null || commits.Count == 0)
return;
AvailableCommits = new ObservableCollection<GitCommit>(commits);
SelectedCommit = AvailableCommits.FirstOrDefault();
1 year ago
UpdateSelectedVersionToLatestMain();
}
InstallName = SelectedPackage.DisplayName;
LatestVersionText = IsReleaseMode
? $"Latest version: {SelectedVersion?.TagName}"
: $"Branch: {SelectedVersion?.TagName}";
1 year ago
})
.SafeFireAndForget();
}
1 year ago
partial void OnInstallNameChanged(string? value)
{
1 year ago
ShowDuplicateWarning = settingsManager.Settings.InstalledPackages.Any(
p => p.LibraryPath == $"Packages{Path.DirectorySeparatorChar}{value}"
);
}
1 year ago
partial void OnSelectedVersionChanged(PackageVersion? value)
{
ReleaseNotes = value?.ReleaseNotesMarkdown ?? string.Empty;
1 year ago
if (value == null)
return;
SelectedCommit = null;
AvailableCommits?.Clear();
1 year ago
if (!IsReleaseMode)
{
Task.Run(async () =>
{
1 year ago
try
{
1 year ago
var hashes = await SelectedPackage.GetAllCommits(value.TagName);
if (hashes is null)
throw new Exception("No commits found");
Dispatcher.UIThread.Post(() =>
{
AvailableCommits = new ObservableCollection<GitCommit>(hashes);
SelectedCommit = AvailableCommits[0];
});
}
catch (Exception e)
{
Logger.Warn($"Error getting commits: {e.Message}");
}
})
.SafeFireAndForget();
}
}
}