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.
 
 
 

221 lines
7.6 KiB

using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Threading;
using AvaloniaEdit.Document;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FluentAvalonia.UI.Controls;
using Microsoft.Extensions.Logging;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper.Factory;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Packages;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(LaunchPageView))]
public partial class LaunchPageViewModel : PageViewModelBase, IDisposable
{
private readonly ILogger<LaunchPageViewModel> logger;
private readonly ISettingsManager settingsManager;
private readonly IPackageFactory packageFactory;
private readonly IPyRunner pyRunner;
public override string Title => "Launch";
public override Symbol Icon => Symbol.PlayFilled;
[ObservableProperty]
private TextDocument consoleDocument = new();
[ObservableProperty] private bool launchButtonVisibility;
[ObservableProperty] private bool stopButtonVisibility;
[ObservableProperty] private bool isLaunchTeachingTipsOpen;
[ObservableProperty] private bool showWebUiButton;
[ObservableProperty] private InstalledPackage? selectedPackage;
[ObservableProperty] private ObservableCollection<InstalledPackage> installedPackages = new();
[ObservableProperty] private BasePackage? runningPackage;
// private bool clearingPackages;
private string webUiUrl = string.Empty;
public LaunchPageViewModel(ILogger<LaunchPageViewModel> logger, ISettingsManager settingsManager, IPackageFactory packageFactory,
IPyRunner pyRunner)
{
this.logger = logger;
this.settingsManager = settingsManager;
this.packageFactory = packageFactory;
this.pyRunner = pyRunner;
}
public override void OnLoaded()
{
LoadPackages();
lock (InstalledPackages)
{
// Skip if no packages
if (!InstalledPackages.Any())
{
//logger.LogTrace($"No packages for {nameof(LaunchViewModel)}");
return;
}
var activePackageId = settingsManager.Settings.ActiveInstalledPackage;
if (activePackageId != null)
{
SelectedPackage = InstalledPackages.FirstOrDefault(
x => x.Id == activePackageId) ?? InstalledPackages[0];
}
}
}
[RelayCommand]
private async Task LaunchAsync()
{
var activeInstall = SelectedPackage;
if (activeInstall == null)
{
// No selected package: error snackbar
// snackbarService.ShowSnackbarAsync(
// "You must install and select a package before launching",
// "No package selected").SafeFireAndForget();
return;
}
var activeInstallName = activeInstall.PackageName;
var basePackage = string.IsNullOrWhiteSpace(activeInstallName)
? null
: packageFactory.FindPackageByName(activeInstallName);
if (basePackage == null)
{
logger.LogWarning(
"During launch, package name '{PackageName}' did not match a definition",
activeInstallName);
// snackbarService.ShowSnackbarAsync(
// "Install package name did not match a definition. Please reinstall and let us know about this issue.",
// "Package name invalid").SafeFireAndForget();
return;
}
// If this is the first launch (LaunchArgs is null),
// load and save a launch options dialog in background
// so that dynamic initial values are saved.
// if (activeInstall.LaunchArgs == null)
// {
// var definitions = basePackage.LaunchOptions;
// // Open a config page and save it
// var dialog = dialogFactory.CreateLaunchOptionsDialog(definitions, activeInstall);
// var args = dialog.AsLaunchArgs();
// settingsManager.SaveLaunchArgs(activeInstall.Id, args);
// }
// Clear console
ConsoleDocument.Text = string.Empty;
await pyRunner.Initialize();
// Get path from package
var packagePath = $"{settingsManager.LibraryDir}\\{activeInstall.LibraryPath!}";
basePackage.ConsoleOutput += OnProcessOutputReceived;
basePackage.Exited += OnProcessExited;
// basePackage.StartupComplete += RunningPackageOnStartupComplete;
// Update shared folder links (in case library paths changed)
//sharedFolders.UpdateLinksForPackage(basePackage, packagePath);
// Load user launch args from settings and convert to string
var userArgs = settingsManager.GetLaunchArgs(activeInstall.Id);
var userArgsString = string.Join(" ", userArgs.Select(opt => opt.ToArgString()));
// Join with extras, if any
userArgsString = string.Join(" ", userArgsString, basePackage.ExtraLaunchArguments);
await basePackage.RunPackage(packagePath, userArgsString);
RunningPackage = basePackage;
}
public async Task Stop()
{
if (RunningPackage is null) return;
await RunningPackage.Shutdown();
// runningPackage.StartupComplete -= RunningPackageOnStartupComplete;
RunningPackage = null;
ConsoleDocument.Text += $"{Environment.NewLine}Stopped process at {DateTimeOffset.Now}{Environment.NewLine}";
ShowWebUiButton = false;
}
private void OnProcessExited(object? sender, int exitCode)
{
if (sender is BasePackage package)
{
package.ConsoleOutput -= OnProcessOutputReceived;
package.Exited -= OnProcessExited;
}
RunningPackage = null;
ShowWebUiButton = false;
Dispatcher.UIThread.Post(() =>
{
ConsoleDocument.Text += $"{Environment.NewLine}Process finished with exit code {exitCode}{Environment.NewLine}";
});
}
// Callback for processes
private void OnProcessOutputReceived(object? sender, ProcessOutput output)
{
var raw = output.RawText;
// Replace \n and \r with literals
raw = raw.Replace("\n", "\\n").Replace("\r", "\\r");
Debug.WriteLine($"output: '{raw}', clear lines: {output.ClearLines}");
Debug.Flush();
Dispatcher.UIThread.Post(() =>
{
using var update = ConsoleDocument.RunUpdate();
// Handle remove
if (output.ClearLines > 0)
{
for (var i = 0; i < output.ClearLines; i++)
{
var lastLineIndex = ConsoleDocument.LineCount - 1;
var line = ConsoleDocument.Lines[lastLineIndex];
ConsoleDocument.Remove(line.Offset, line.Length);
}
}
// Add new line
ConsoleDocument.Insert(ConsoleDocument.TextLength, output.Text);
});
}
private void LoadPackages()
{
var packages = settingsManager.Settings.InstalledPackages;
if (!packages?.Any() ?? true)
{
InstalledPackages.Clear();
return;
}
InstalledPackages.Clear();
foreach (var package in packages)
{
InstalledPackages.Add(package);
}
}
public void Dispose()
{
RunningPackage?.Shutdown();
GC.SuppressFinalize(this);
}
}