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.
 
 
 

253 lines
8.1 KiB

using System.Text.Json.Serialization;
using StabilityMatrix.Core.Helper;
namespace StabilityMatrix.Core.Models;
/// <summary>
/// Profile information for a user-installed package.
/// </summary>
public class InstalledPackage : IJsonOnDeserialized
{
// Unique ID for the installation
public Guid Id { get; set; }
// User defined name
public string? DisplayName { get; set; }
// Package name
public string? PackageName { get; set; }
// Package version
[Obsolete("Use Version instead. (Kept for migration)")]
public string? PackageVersion { get; set; }
[Obsolete("Use Version instead. (Kept for migration)")]
public string? InstalledBranch { get; set; }
[Obsolete("Use Version instead. (Kept for migration)")]
public string? DisplayVersion { get; set; }
public InstalledPackageVersion? Version { get; set; }
// Old type absolute path
[Obsolete("Use LibraryPath instead. (Kept for migration)")]
public string? Path { get; set; }
/// <summary>
/// Relative path from the library root.
/// </summary>
public string? LibraryPath { get; set; }
/// <summary>
/// Full path to the package, using LibraryPath and GlobalConfig.LibraryDir.
/// </summary>
[JsonIgnore]
public string? FullPath =>
LibraryPath != null ? System.IO.Path.Combine(GlobalConfig.LibraryDir, LibraryPath) : null;
public string? LaunchCommand { get; set; }
public List<LaunchOption>? LaunchArgs { get; set; }
public DateTimeOffset? LastUpdateCheck { get; set; }
public bool UpdateAvailable { get; set; }
public TorchVersion? PreferredTorchVersion { get; set; }
public SharedFolderMethod? PreferredSharedFolderMethod { get; set; }
/// <summary>
/// Get the launch args host option value.
/// </summary>
public string? GetLaunchArgsHost()
{
var hostOption = LaunchArgs?.FirstOrDefault(x => x.Name.ToLowerInvariant() == "host");
if (hostOption?.OptionValue != null)
{
return hostOption.OptionValue as string;
}
return hostOption?.DefaultValue as string;
}
/// <summary>
/// Get the launch args port option value.
/// </summary>
public string? GetLaunchArgsPort()
{
var portOption = LaunchArgs?.FirstOrDefault(x => x.Name.ToLowerInvariant() == "port");
if (portOption?.OptionValue != null)
{
return portOption.OptionValue as string;
}
return portOption?.DefaultValue as string;
}
/// <summary>
/// Get the path as a relative sub-path of the relative path.
/// If not a sub-path, return null.
/// </summary>
public static string? GetSubPath(string relativeTo, string path)
{
var relativePath = System.IO.Path.GetRelativePath(relativeTo, path);
// GetRelativePath returns the path if it's not relative
if (relativePath == path)
return null;
// Further check if the path is a sub-path of the library
var isSubPath =
relativePath != "."
&& relativePath != ".."
&& !relativePath.StartsWith(".." + System.IO.Path.DirectorySeparatorChar)
&& !System.IO.Path.IsPathRooted(relativePath);
return isSubPath ? relativePath : null;
}
/// <summary>
/// Migrates the old Path to the new LibraryPath.
/// If libraryDirectory is null, GlobalConfig.LibraryDir is used.
/// </summary>
/// <returns>True if the path was migrated, false otherwise.</returns>
public bool TryPureMigratePath(string? libraryDirectory = null)
{
#pragma warning disable CS0618
var oldPath = Path;
#pragma warning restore CS0618
if (oldPath == null)
return false;
// Check if the path is a sub-path of the library
var library = libraryDirectory ?? GlobalConfig.LibraryDir;
var relativePath = GetSubPath(library, oldPath);
// If so we migrate without any IO operations
if (relativePath != null)
{
LibraryPath = relativePath;
#pragma warning disable CS0618
Path = null;
#pragma warning restore CS0618
return true;
}
return false;
}
/// <summary>
/// Check if the old Path can be migrated to the new LibraryPath.
/// </summary>
/// <param name="libraryDirectory"></param>
/// <returns></returns>
public bool CanPureMigratePath(string? libraryDirectory = null)
{
#pragma warning disable CS0618
var oldPath = Path;
#pragma warning restore CS0618
if (oldPath == null)
return false;
// Check if the path is a sub-path of the library
var library = libraryDirectory ?? GlobalConfig.LibraryDir;
var relativePath = GetSubPath(library, oldPath);
return relativePath != null;
}
/// <summary>
/// Migrate the old Path to the new LibraryPath.
/// If libraryDirectory is null, GlobalConfig.LibraryDir is used.
/// Will move the package directory to Library/Packages if not relative.
/// </summary>
public async Task MigratePath(string? libraryDirectory = null)
{
#pragma warning disable CS0618
var oldPath = Path;
#pragma warning restore CS0618
if (oldPath == null)
return;
var libDir = libraryDirectory ?? GlobalConfig.LibraryDir;
// if old package Path is same as new library, return
if (oldPath.Replace(DisplayName, "") == libDir)
{
// Update the paths
#pragma warning disable CS0618
Path = null;
#pragma warning restore CS0618
LibraryPath = System.IO.Path.Combine("Packages", DisplayName);
return;
}
// Try using pure migration first
if (TryPureMigratePath(libraryDirectory))
return;
// If not, we need to move the package directory
var packageFolderName = new DirectoryInfo(oldPath).Name;
// Get the new Library/Packages path
var library = libraryDirectory ?? GlobalConfig.LibraryDir;
var newPackagesDir = System.IO.Path.Combine(library, "Packages");
// Get the new target path
var newPackagePath = System.IO.Path.Combine(newPackagesDir, packageFolderName);
// Ensure it is not already there, if so, add a suffix until it's not
var suffix = 2;
while (Directory.Exists(newPackagePath))
{
newPackagePath = System.IO.Path.Combine(
newPackagesDir,
$"{packageFolderName}-{suffix}"
);
suffix++;
}
// Move the package directory
await Task.Run(() => Utilities.CopyDirectory(oldPath, newPackagePath, true));
// Update the paths
#pragma warning disable CS0618
Path = null;
#pragma warning restore CS0618
LibraryPath = System.IO.Path.Combine("Packages", packageFolderName);
}
public static IEqualityComparer<InstalledPackage> Comparer { get; } =
new PropertyComparer<InstalledPackage>(p => p.Id);
protected bool Equals(InstalledPackage other)
{
return Id.Equals(other.Id);
}
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
return false;
if (ReferenceEquals(this, obj))
return true;
return obj.GetType() == this.GetType() && Equals((InstalledPackage)obj);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
#pragma warning disable CS0618 // Type or member is obsolete
public void OnDeserialized()
{
// Handle version migration
if (Version != null)
return;
if (
string.IsNullOrWhiteSpace(InstalledBranch) && !string.IsNullOrWhiteSpace(PackageVersion)
)
{
// release mode
Version = new InstalledPackageVersion { InstalledReleaseVersion = PackageVersion };
}
else if (!string.IsNullOrWhiteSpace(PackageVersion))
{
Version = new InstalledPackageVersion
{
InstalledBranch = InstalledBranch,
InstalledCommitSha = PackageVersion
};
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}