using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; using StabilityMatrix.Avalonia.Languages; using StabilityMatrix.Avalonia.ViewModels.Base; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Factory; using StabilityMatrix.Core.Models; using StabilityMatrix.Core.Models.Packages; using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; namespace StabilityMatrix.Avalonia.ViewModels.Dialogs; public partial class OneClickInstallViewModel : ViewModelBase { private readonly ISettingsManager settingsManager; private readonly IPackageFactory packageFactory; private readonly IPrerequisiteHelper prerequisiteHelper; private readonly ILogger logger; private readonly IPyRunner pyRunner; private readonly ISharedFolders sharedFolders; private const string DefaultPackageName = "stable-diffusion-webui"; [ObservableProperty] private string headerText; [ObservableProperty] private string subHeaderText; [ObservableProperty] private string subSubHeaderText = string.Empty; [ObservableProperty] private bool showInstallButton; [ObservableProperty] private bool isIndeterminate; [ObservableProperty] private ObservableCollection allPackages; [ObservableProperty] private BasePackage selectedPackage; [ObservableProperty] [NotifyPropertyChangedFor(nameof(IsProgressBarVisible))] private int oneClickInstallProgress; public bool IsProgressBarVisible => OneClickInstallProgress > 0 || IsIndeterminate; public OneClickInstallViewModel( ISettingsManager settingsManager, IPackageFactory packageFactory, IPrerequisiteHelper prerequisiteHelper, ILogger logger, IPyRunner pyRunner, ISharedFolders sharedFolders ) { this.settingsManager = settingsManager; this.packageFactory = packageFactory; this.prerequisiteHelper = prerequisiteHelper; this.logger = logger; this.pyRunner = pyRunner; this.sharedFolders = sharedFolders; HeaderText = Resources.Text_WelcomeToStabilityMatrix; SubHeaderText = Resources.Text_OneClickInstaller_SubHeader; ShowInstallButton = true; AllPackages = new ObservableCollection( this.packageFactory.GetAllAvailablePackages().Where(p => p.OfferInOneClickInstaller) ); SelectedPackage = AllPackages[0]; } [RelayCommand] private async Task Install() { ShowInstallButton = false; await DoInstall(); ShowInstallButton = true; } [RelayCommand] private Task ToggleAdvancedMode() { EventManager.Instance.OnOneClickInstallFinished(true); return Task.CompletedTask; } private async Task DoInstall() { HeaderText = $"{Resources.Label_Installing} {SelectedPackage.DisplayName}"; var progressHandler = new Progress(progress => { SubHeaderText = $"{progress.Title} {progress.Percentage:N0}%"; IsIndeterminate = progress.IsIndeterminate; OneClickInstallProgress = Convert.ToInt32(progress.Percentage); }); await prerequisiteHelper.InstallAllIfNecessary(progressHandler); SubHeaderText = Resources.Progress_InstallingPrerequisites; IsIndeterminate = true; if (!PyRunner.PipInstalled) { await pyRunner.SetupPip(); } if (!PyRunner.VenvInstalled) { await pyRunner.InstallPackage("virtualenv"); } IsIndeterminate = false; var libraryDir = settingsManager.LibraryDir; // get latest version & download & install var installLocation = Path.Combine(libraryDir, "Packages", SelectedPackage.Name); var downloadVersion = new DownloadPackageVersionOptions(); var installedVersion = new InstalledPackageVersion(); var versionOptions = await SelectedPackage.GetAllVersionOptions(); if (versionOptions.AvailableVersions != null && versionOptions.AvailableVersions.Any()) { downloadVersion.VersionTag = versionOptions.AvailableVersions.First().TagName; installedVersion.InstalledReleaseVersion = downloadVersion.VersionTag; } else { downloadVersion.BranchName = await SelectedPackage.GetLatestVersion(); installedVersion.InstalledBranch = downloadVersion.BranchName; } var torchVersion = SelectedPackage.GetRecommendedTorchVersion(); await DownloadPackage(installLocation, downloadVersion); await InstallPackage(installLocation, torchVersion); var recommendedSharedFolderMethod = SelectedPackage.RecommendedSharedFolderMethod; await SelectedPackage.SetupModelFolders(installLocation, recommendedSharedFolderMethod); var installedPackage = new InstalledPackage { DisplayName = SelectedPackage.DisplayName, LibraryPath = Path.Combine("Packages", SelectedPackage.Name), Id = Guid.NewGuid(), PackageName = SelectedPackage.Name, Version = installedVersion, LaunchCommand = SelectedPackage.LaunchCommand, LastUpdateCheck = DateTimeOffset.Now, PreferredTorchVersion = torchVersion, PreferredSharedFolderMethod = recommendedSharedFolderMethod }; await using var st = settingsManager.BeginTransaction(); st.Settings.InstalledPackages.Add(installedPackage); st.Settings.ActiveInstalledPackageId = installedPackage.Id; EventManager.Instance.OnInstalledPackagesChanged(); HeaderText = Resources.Progress_InstallationComplete; SubSubHeaderText = string.Empty; OneClickInstallProgress = 100; for (var i = 0; i < 3; i++) { SubHeaderText = $"{Resources.Text_ProceedingToLaunchPage} ({i + 1}s)"; await Task.Delay(1000); } // should close dialog EventManager.Instance.OnOneClickInstallFinished(false); } private async Task DownloadPackage( string installLocation, DownloadPackageVersionOptions versionOptions ) { SubHeaderText = Resources.Progress_DownloadingPackage; var progress = new Progress(progress => { IsIndeterminate = progress.IsIndeterminate; OneClickInstallProgress = Convert.ToInt32(progress.Percentage); EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); await SelectedPackage.DownloadPackage(installLocation, versionOptions, progress); SubHeaderText = Resources.Progress_DownloadComplete; OneClickInstallProgress = 100; } private async Task InstallPackage(string installLocation, TorchVersion torchVersion) { var progress = new Progress(progress => { SubHeaderText = Resources.Progress_InstallingPackageRequirements; IsIndeterminate = progress.IsIndeterminate; OneClickInstallProgress = Convert.ToInt32(progress.Percentage); EventManager.Instance.OnGlobalProgressChanged(OneClickInstallProgress); }); await SelectedPackage.InstallPackage( installLocation, torchVersion, progress, (output) => SubSubHeaderText = output.Text ); } }