@ -17,10 +17,12 @@ using FluentAvalonia.UI.Controls;
using NLog ;
using NLog ;
using StabilityMatrix.Avalonia.Controls ;
using StabilityMatrix.Avalonia.Controls ;
using StabilityMatrix.Avalonia.Services ;
using StabilityMatrix.Avalonia.Services ;
using StabilityMatrix.Avalonia.ViewModels.Base ;
using StabilityMatrix.Core.Helper ;
using StabilityMatrix.Core.Helper ;
using StabilityMatrix.Core.Helper.Factory ;
using StabilityMatrix.Core.Helper.Factory ;
using StabilityMatrix.Core.Models ;
using StabilityMatrix.Core.Models ;
using StabilityMatrix.Core.Models.Database ;
using StabilityMatrix.Core.Models.Database ;
using StabilityMatrix.Core.Models.PackageModification ;
using StabilityMatrix.Core.Models.Packages ;
using StabilityMatrix.Core.Models.Packages ;
using StabilityMatrix.Core.Models.Progress ;
using StabilityMatrix.Core.Models.Progress ;
using StabilityMatrix.Core.Processes ;
using StabilityMatrix.Core.Processes ;
@ -29,7 +31,6 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs ;
namespace StabilityMatrix.Avalonia.ViewModels.Dialogs ;
public partial class InstallerViewModel : ContentDialogViewModelBase
public partial class InstallerViewModel : ContentDialogViewModelBase
{
{
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
private static readonly Logger Logger = LogManager . GetCurrentClassLogger ( ) ;
@ -40,48 +41,88 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
private readonly INotificationService notificationService ;
private readonly INotificationService notificationService ;
private readonly IPrerequisiteHelper prerequisiteHelper ;
private readonly IPrerequisiteHelper prerequisiteHelper ;
[ObservableProperty] private BasePackage selectedPackage ;
[ObservableProperty]
[ObservableProperty] private PackageVersion ? selectedVersion ;
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 IReadOnlyList < BasePackage > ? availablePackages ;
[ObservableProperty]
[ObservableProperty] private ObservableCollection < GitCommit > ? availableCommits ;
private string latestVersionText = string . Empty ;
[ObservableProperty] private ObservableCollection < PackageVersion > ? availableVersions ;
[ObservableProperty] private GitCommit ? selectedCommit ;
[ObservableProperty]
private bool isAdvancedMode ;
[ObservableProperty] private string? releaseNotes ;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanInstall))]
private bool showDuplicateWarning ;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanInstall))]
private string? installName ;
[ObservableProperty]
private SharedFolderMethod selectedSharedFolderMethod ;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ShowTorchVersionOptions))]
private TorchVersion selectedTorchVersion ;
// Version types (release or commit)
// Version types (release or commit)
[ObservableProperty]
[ObservableProperty]
[ NotifyPropertyChangedFor ( nameof ( ReleaseLabelText ) ,
[ NotifyPropertyChangedFor (
nameof ( IsReleaseMode ) , nameof ( SelectedVersion ) ) ]
nameof ( ReleaseLabelText ) ,
nameof ( IsReleaseMode ) ,
nameof ( SelectedVersion )
) ]
private PackageVersionType selectedVersionType = PackageVersionType . Commit ;
private PackageVersionType selectedVersionType = PackageVersionType . Commit ;
[ObservableProperty]
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsReleaseModeAvailable))]
[NotifyPropertyChangedFor(nameof(IsReleaseModeAvailable))]
private PackageVersionType availableVersionTypes =
private PackageVersionType availableVersionTypes =
PackageVersionType . GithubRelease | PackageVersionType . Commit ;
PackageVersionType . GithubRelease | PackageVersionType . Commit ;
public string ReleaseLabelText = > IsReleaseMode ? "Version" : "Branch" ;
public string ReleaseLabelText = > IsReleaseMode ? "Version" : "Branch" ;
public bool IsReleaseMode
public bool IsReleaseMode
{
{
get = > SelectedVersionType = = PackageVersionType . GithubRelease ;
get = > SelectedVersionType = = PackageVersionType . GithubRelease ;
set = > SelectedVersionType = value ? PackageVersionType . GithubRelease : PackageVersionType . Commit ;
set = >
SelectedVersionType = value
? PackageVersionType . GithubRelease
: PackageVersionType . Commit ;
}
}
public bool IsReleaseModeAvailable = >
AvailableVersionTypes . HasFlag ( PackageVersionType . GithubRelease ) ;
public bool ShowTorchVersionOptions = > SelectedTorchVersion ! = TorchVersion . None ;
public bool IsReleaseModeAvailable = > AvailableVersionTypes . HasFlag ( PackageVersionType . GithubRelease ) ;
public bool CanInstall = > ! string . IsNullOrWhiteSpace ( InstallName ) & & ! ShowDuplicateWarning ;
[ObservableProperty] private bool showDuplicateWarning ;
[ObservableProperty] private string? installName ;
public Base . ProgressViewModel InstallProgress { get ; } = new ( ) ;
public ProgressViewModel InstallProgress { get ; } = new ( ) ;
public IEnumerable < IPackageStep > Steps { get ; set ; }
public InstallerViewModel (
public InstallerViewModel (
ISettingsManager settingsManager ,
ISettingsManager settingsManager ,
IPackageFactory packageFactory ,
IPackageFactory packageFactory ,
IPyRunner pyRunner ,
IPyRunner pyRunner ,
IDownloadService downloadService , INotificationService notificationService ,
IDownloadService downloadService ,
IPrerequisiteHelper prerequisiteHelper )
INotificationService notificationService ,
IPrerequisiteHelper prerequisiteHelper
)
{
{
this . settingsManager = settingsManager ;
this . settingsManager = settingsManager ;
this . pyRunner = pyRunner ;
this . pyRunner = pyRunner ;
@ -90,40 +131,43 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
this . prerequisiteHelper = prerequisiteHelper ;
this . prerequisiteHelper = prerequisiteHelper ;
// AvailablePackages and SelectedPackage
// AvailablePackages and SelectedPackage
AvailablePackages = new ObservableCollection < BasePackage > ( packageFactory . GetAllAvailablePackages ( ) ) ;
AvailablePackages = new ObservableCollection < BasePackage > (
packageFactory . GetAllAvailablePackages ( )
) ;
SelectedPackage = AvailablePackages [ 0 ] ;
SelectedPackage = AvailablePackages [ 0 ] ;
}
}
public override void OnLoaded ( )
public override void OnLoaded ( )
{
{
if ( AvailablePackages = = null ) return ;
if ( AvailablePackages = = null )
return ;
SelectedPackage = AvailablePackages [ 0 ] ;
SelectedPackage = AvailablePackages [ 0 ] ;
IsReleaseMode = ! SelectedPackage . ShouldIgnoreReleases ;
IsReleaseMode = ! SelectedPackage . ShouldIgnoreReleases ;
}
}
public override async Task OnLoadedAsync ( )
public override async Task OnLoadedAsync ( )
{
{
if ( Design . IsDesignMode ) return ;
if ( Design . IsDesignMode )
return ;
// Check for updates
// Check for updates
try
try
{
{
var versionOptions = await SelectedPackage . GetAllVersionOptions ( ) ;
if ( IsReleaseMode )
if ( IsReleaseMode )
{
{
var versions = ( await SelectedPackage . GetAllVersions ( ) ) . ToList ( ) ;
AvailableVersions = new ObservableCollection < PackageVersion > (
AvailableVersions = new ObservableCollection < PackageVersion > ( versions ) ;
versionOptions . AvailableVersions
if ( ! AvailableVersions . Any ( ) ) return ;
) ;
if ( ! AvailableVersions . Any ( ) )
return ;
SelectedVersion = AvailableVersions [ 0 ] ;
SelectedVersion = AvailableVersions . First ( x = > ! x . IsPrerelease ) ;
}
}
else
else
{
{
var branches = ( await SelectedPackage . GetAllBranches ( ) ) . ToList ( ) ;
AvailableVersions = new ObservableCollection < PackageVersion > (
AvailableVersions = new ObservableCollection < PackageVersion > ( branches . Select ( b = >
versionOptions . AvailableBranches
new PackageVersion
) ;
{
TagName = b . Name ,
ReleaseNotesMarkdown = b . Commit . Label
} ) ) ;
UpdateSelectedVersionToLatestMain ( ) ;
UpdateSelectedVersionToLatestMain ( ) ;
}
}
@ -138,12 +182,12 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
[RelayCommand]
[RelayCommand]
private async Task Install ( )
private async Task Install ( )
{
{
var result = await notificationService . TryAsync ( ActuallyInstall ( ) , "Could not install package" ) ;
var result = await notificationService . TryAsync (
ActuallyInstall ( ) ,
"Could not install package"
) ;
if ( result . IsSuccessful )
if ( result . IsSuccessful )
{
{
notificationService . Show ( new Notification (
$"Package {SelectedPackage.Name} installed successfully!" ,
"Success" , NotificationType . Success ) ) ;
OnPrimaryButtonClick ( ) ;
OnPrimaryButtonClick ( ) ;
}
}
else
else
@ -161,67 +205,59 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
}
}
}
}
private async Task ActuallyInstall ( )
private Task ActuallyInstall ( )
{
{
if ( string . IsNullOrWhiteSpace ( InstallName ) )
if ( string . IsNullOrWhiteSpace ( InstallName ) )
{
{
notificationService . Show ( new Notification ( "Package name is empty" ,
notificationService . Show (
"Please enter a name for the package" , NotificationType . Error ) ) ;
new Notification (
return ;
"Package name is empty" ,
"Please enter a name for the package" ,
NotificationType . Error
)
) ;
return Task . CompletedTask ;
}
}
try
var setPackageInstallingStep = new SetPackageInstallingStep ( settingsManager , InstallName ) ;
{
await InstallGitIfNecessary ( ) ;
SelectedPackage . InstallLocation = Path . Combine (
var installLocation = Path . Combine ( settingsManager . LibraryDir , "Packages" , InstallName ) ;
settingsManager . LibraryDir , "Packages" , InstallName ) ;
var prereqStep = new SetupPrerequisitesStep ( prerequisiteHelper , pyRunner ) ;
if ( ! PyRunner . PipInstalled | | ! PyRunner . VenvInstalled )
var downloadOptions = new DownloadPackageVersionOptions ( ) ;
{
var installedVersion = new InstalledPackageVersion ( ) ;
InstallProgress . Text = "Installing dependencies..." ;
InstallProgress . IsIndeterminate = true ;
await pyRunner . Initialize ( ) ;
if ( ! PyRunner . PipInstalled )
{
await pyRunner . SetupPip ( ) ;
}
if ( ! PyRunner . VenvInstalled )
{
await pyRunner . InstallPackage ( "virtualenv" ) ;
}
}
string version ;
if ( IsReleaseMode )
if ( IsReleaseMode )
{
{
version = SelectedVersion ? . TagName ? ?
downloadOptions . VersionTag =
throw new NullReferenceException ( "Selected version is null" ) ;
SelectedVersion ? . TagName
? ? throw new NullReferenceException ( "Selected version is null" ) ;
await DownloadPackage ( version , false , null ) ;
installedVersion . InstalledReleaseVersion = downloadOptions . VersionTag ;
}
}
else
else
{
{
version = SelectedCommit ? . Sha ? ?
downloadOptions . CommitHash =
throw new NullReferenceException ( "Selected commit is null" ) ;
SelectedCommit ? . Sha ? ? throw new NullReferenceException ( "Selected commit is null" ) ;
installedVersion . InstalledBranch =
await DownloadPackage ( version , true , SelectedVersion ! . TagName ) ;
SelectedVersion ? . TagName
}
? ? throw new NullReferenceException ( "Selected version is null" ) ;
installedVersion . InstalledCommitSha = downloadOptions . CommitHash ;
await InstallPackage ( ) ;
}
InstallProgress . Text = "Setting up shared folder links..." ;
var downloadStep = new DownloadPackageVersionStep (
await SelectedPackage . SetupModelFolders ( SelectedPackage . InstallLocation ) ;
SelectedPackage ,
//sharedFolders.SetupLinksForPackage(SelectedPackage, SelectedPackage.InstallLocation);
installLocation ,
downloadOptions
InstallProgress . Text = "Done" ;
) ;
InstallProgress . IsIndeterminate = false ;
var installStep = new InstallPackageStep (
InstallProgress . Value = 1 0 0 ;
SelectedPackage ,
EventManager . Instance . OnGlobalProgressChanged ( 1 0 0 ) ;
SelectedTorchVersion ,
installLocation
var branch = SelectedVersionType = = PackageVersionType . GithubRelease ?
) ;
null : SelectedVersion ! . TagName ;
var setupModelFoldersStep = new SetupModelFoldersStep (
SelectedPackage ,
SelectedSharedFolderMethod ,
installLocation
) ;
var package = new InstalledPackage
var package = new InstalledPackage
{
{
@ -229,21 +265,32 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
LibraryPath = Path . Combine ( "Packages" , InstallName ) ,
LibraryPath = Path . Combine ( "Packages" , InstallName ) ,
Id = Guid . NewGuid ( ) ,
Id = Guid . NewGuid ( ) ,
PackageName = SelectedPackage . Name ,
PackageName = SelectedPackage . Name ,
PackageVersion = version ,
Version = installedVersion ,
DisplayVersion = GetDisplayVersion ( version , branch ) ,
InstalledBranch = branch ,
LaunchCommand = SelectedPackage . LaunchCommand ,
LaunchCommand = SelectedPackage . LaunchCommand ,
LastUpdateCheck = DateTimeOffset . Now
LastUpdateCheck = DateTimeOffset . Now ,
PreferredTorchVersion = SelectedTorchVersion ,
PreferredSharedFolderMethod = SelectedSharedFolderMethod
} ;
} ;
await using var st = settingsManager . BeginTransaction ( ) ;
st . Settings . InstalledPackages . Add ( package ) ;
var addInstalledPackageStep = new AddInstalledPackageStep ( settingsManager , package ) ;
st . Settings . ActiveInstalledPackageId = package . Id ;
}
var steps = new List < IPackageStep >
finally
{
{
InstallProgress . Value = 0 ;
setPackageInstallingStep ,
InstallProgress . IsIndeterminate = false ;
prereqStep ,
downloadStep ,
installStep ,
setupModelFoldersStep ,
addInstalledPackageStep
} ;
Steps = steps ;
return Task . CompletedTask ;
}
}
public void Cancel ( )
{
OnCloseButtonClick ( ) ;
}
}
private void UpdateSelectedVersionToLatestMain ( )
private void UpdateSelectedVersionToLatestMain ( )
@ -260,57 +307,12 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
version ? ? = AvailableVersions . FirstOrDefault ( x = > x . TagName = = "main" ) ;
version ? ? = AvailableVersions . FirstOrDefault ( x = > x . TagName = = "main" ) ;
// If still not found, just use the first one
// If still not found, just use the first one
version ? ? = AvailableVersions [ 0 ] ;
version ? ? = AvailableVersions . FirstOrDefault ( ) ;
SelectedVersion = version ;
SelectedVersion = version ;
}
}
}
}
private static string GetDisplayVersion ( string version , string? branch )
{
return branch = = null ? version : $"{branch}@{version[..7]}" ;
}
private Task < string > DownloadPackage ( string version , bool isCommitHash , string? branch )
{
InstallProgress . Text = "Downloading package..." ;
var progress = new Progress < ProgressReport > ( progress = >
{
InstallProgress . IsIndeterminate = progress . IsIndeterminate ;
InstallProgress . Value = progress . Percentage ;
EventManager . Instance . OnGlobalProgressChanged ( ( int ) progress . Percentage ) ;
} ) ;
return SelectedPackage . DownloadPackage ( version , isCommitHash , branch , progress ) ;
}
private async Task InstallPackage ( )
{
InstallProgress . Text = "Installing package..." ;
SelectedPackage . ConsoleOutput + = SelectedPackageOnConsoleOutput ;
try
{
var progress = new Progress < ProgressReport > ( progress = >
{
InstallProgress . IsIndeterminate = progress . IsIndeterminate ;
InstallProgress . Value = progress . Percentage ;
EventManager . Instance . OnGlobalProgressChanged ( ( int ) progress . Percentage ) ;
} ) ;
await SelectedPackage . InstallPackage ( progress ) ;
}
finally
{
SelectedPackage . ConsoleOutput - = SelectedPackageOnConsoleOutput ;
}
}
private void SelectedPackageOnConsoleOutput ( object? sender , ProcessOutput e )
{
InstallProgress . Description = e . Text ;
}
[RelayCommand]
[RelayCommand]
private async Task ShowPreview ( )
private async Task ShowPreview ( )
{
{
@ -349,7 +351,8 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
// When changing branch / release modes, refresh
// When changing branch / release modes, refresh
// ReSharper disable once UnusedParameterInPartialMethod
// ReSharper disable once UnusedParameterInPartialMethod
partial void OnSelectedVersionTypeChanged ( PackageVersionType value ) = > OnSelectedPackageChanged ( SelectedPackage ) ;
partial void OnSelectedVersionTypeChanged ( PackageVersionType value ) = >
OnSelectedPackageChanged ( SelectedPackage ) ;
partial void OnSelectedPackageChanged ( BasePackage value )
partial void OnSelectedPackageChanged ( BasePackage value )
{
{
@ -361,74 +364,58 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
? PackageVersionType . Commit
? PackageVersionType . Commit
: PackageVersionType . GithubRelease | PackageVersionType . Commit ;
: PackageVersionType . GithubRelease | PackageVersionType . Commit ;
if ( Design . IsDesignMode ) return ;
SelectedSharedFolderMethod = SelectedPackage . RecommendedSharedFolderMethod ;
SelectedTorchVersion = SelectedPackage . GetRecommendedTorchVersion ( ) ;
Dispatcher . UIThread . InvokeAsync ( async ( ) = >
if ( Design . IsDesignMode )
return ;
Dispatcher . UIThread
. InvokeAsync ( async ( ) = >
{
{
Logger . Debug ( $"Release mode: {IsReleaseMode}" ) ;
Logger . Debug ( $"Release mode: {IsReleaseMode}" ) ;
var versions = ( await value . GetAllVersions ( IsReleaseMode ) ) . ToList ( ) ;
var versionOptions = await value . GetAllVersionOptions ( ) ;
AvailableVersions = IsReleaseMode
? new ObservableCollection < PackageVersion > ( versionOptions . AvailableVersions )
: new ObservableCollection < PackageVersion > ( versionOptions . AvailableBranches ) ;
if ( ! versions . Any ( ) ) return ;
SelectedVersion = AvailableVersions . First ( x = > ! x . IsPrerelease ) ;
ReleaseNotes = SelectedVersion . ReleaseNotesMarkdown ;
AvailableVersions = new ObservableCollection < PackageVersion > ( versions ) ;
Logger . Debug ( $"Available versions: {string.Join(" , ", AvailableVersions)}" ) ;
SelectedVersion = AvailableVersions [ 0 ] ;
ReleaseNotes = versions . First ( ) . ReleaseNotesMarkdown ;
Logger . Debug ( $"Loaded release notes for {ReleaseNotes}" ) ;
Logger . Debug ( $"Loaded release notes for {ReleaseNotes}" ) ;
if ( ! IsReleaseMode )
if ( ! IsReleaseMode )
{
{
var commits = ( await value . GetAllCommits ( SelectedVersion . TagName ) ) ? . ToList ( ) ;
var commits = ( await value . GetAllCommits ( SelectedVersion . TagName ) ) ? . ToList ( ) ;
if ( commits is null | | commits . Count = = 0 ) return ;
if ( commits is null | | commits . Count = = 0 )
return ;
AvailableCommits = new ObservableCollection < GitCommit > ( commits ) ;
AvailableCommits = new ObservableCollection < GitCommit > ( commits ) ;
SelectedCommit = AvailableCommits [ 0 ] ;
SelectedCommit = AvailableCommits . FirstOrDefault ( ) ;
UpdateSelectedVersionToLatestMain ( ) ;
UpdateSelectedVersionToLatestMain ( ) ;
}
}
InstallName = SelectedPackage . DisplayName ;
InstallName = SelectedPackage . DisplayName ;
} ) . SafeFireAndForget ( ) ;
LatestVersionText = IsReleaseMode
}
? $"Latest version: {SelectedVersion?.TagName}"
: $"Branch: {SelectedVersion?.TagName}" ;
private async Task InstallGitIfNecessary ( )
} )
{
. SafeFireAndForget ( ) ;
var progressHandler = new Progress < ProgressReport > ( progress = >
{
if ( progress . Message ! = null & & progress . Message . Contains ( "Downloading" ) )
{
InstallProgress . Text = $"Downloading prerequisites... {progress.Percentage:N0}%" ;
}
else if ( progress . Type = = ProgressType . Extract )
{
InstallProgress . Text = $"Installing git... {progress.Percentage:N0}%" ;
}
else if ( progress . Title ! = null & & progress . Title . Contains ( "Unpacking" ) )
{
InstallProgress . Text = $"Unpacking resources... {progress.Percentage:N0}%" ;
}
else
{
InstallProgress . Text = progress . Message ;
}
InstallProgress . IsIndeterminate = progress . IsIndeterminate ;
InstallProgress . Value = Convert . ToInt32 ( progress . Percentage ) ;
} ) ;
await prerequisiteHelper . InstallAllIfNecessary ( progressHandler ) ;
}
}
partial void OnInstallNameChanged ( string? value )
partial void OnInstallNameChanged ( string? value )
{
{
ShowDuplicateWarning =
ShowDuplicateWarning = settingsManager . Settings . InstalledPackages . Any (
settingsManager . Settings . InstalledPackages . Any ( p = >
p = > p . LibraryPath = = $"Packages{Path.DirectorySeparatorChar}{value}"
p . LibraryPath = = $"Packages{Path.DirectorySeparatorChar}{value}" ) ;
) ;
}
}
partial void OnSelectedVersionChanged ( PackageVersion ? value )
partial void OnSelectedVersionChanged ( PackageVersion ? value )
{
{
ReleaseNotes = value ? . ReleaseNotesMarkdown ? ? string . Empty ;
ReleaseNotes = value ? . ReleaseNotesMarkdown ? ? string . Empty ;
if ( value = = null ) return ;
if ( value = = null )
return ;
SelectedCommit = null ;
SelectedCommit = null ;
AvailableCommits ? . Clear ( ) ;
AvailableCommits ? . Clear ( ) ;
@ -440,7 +427,8 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
try
try
{
{
var hashes = await SelectedPackage . GetAllCommits ( value . TagName ) ;
var hashes = await SelectedPackage . GetAllCommits ( value . TagName ) ;
if ( hashes is null ) throw new Exception ( "No commits found" ) ;
if ( hashes is null )
throw new Exception ( "No commits found" ) ;
Dispatcher . UIThread . Post ( ( ) = >
Dispatcher . UIThread . Post ( ( ) = >
{
{
@ -452,7 +440,8 @@ public partial class InstallerViewModel : ContentDialogViewModelBase
{
{
Logger . Warn ( $"Error getting commits: {e.Message}" ) ;
Logger . Warn ( $"Error getting commits: {e.Message}" ) ;
}
}
} ) . SafeFireAndForget ( ) ;
} )
. SafeFireAndForget ( ) ;
}
}
}
}
}
}