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.
386 lines
14 KiB
386 lines
14 KiB
using System.Diagnostics; |
|
using System.Text.RegularExpressions; |
|
using FreneticUtilities.FreneticDataSyntax; |
|
using StabilityMatrix.Core.Attributes; |
|
using StabilityMatrix.Core.Exceptions; |
|
using StabilityMatrix.Core.Helper; |
|
using StabilityMatrix.Core.Helper.Cache; |
|
using StabilityMatrix.Core.Models.FDS; |
|
using StabilityMatrix.Core.Models.FileInterfaces; |
|
using StabilityMatrix.Core.Models.Progress; |
|
using StabilityMatrix.Core.Processes; |
|
using StabilityMatrix.Core.Services; |
|
|
|
namespace StabilityMatrix.Core.Models.Packages; |
|
|
|
[Singleton(typeof(BasePackage))] |
|
public class StableSwarm( |
|
IGithubApiCache githubApi, |
|
ISettingsManager settingsManager, |
|
IDownloadService downloadService, |
|
IPrerequisiteHelper prerequisiteHelper |
|
) : BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper) |
|
{ |
|
private Process? dotnetProcess; |
|
|
|
public override string Name => "StableSwarmUI"; |
|
public override string DisplayName { get; set; } = "StableSwarmUI"; |
|
public override string Author => "Stability-AI"; |
|
|
|
public override string Blurb => |
|
"A Modular Stable Diffusion Web-User-Interface, with an emphasis on making powertools easily accessible, high performance, and extensibility."; |
|
|
|
public override string LicenseType => "MIT"; |
|
public override string LicenseUrl => |
|
"https://github.com/Stability-AI/StableSwarmUI/blob/master/LICENSE.txt"; |
|
public override string LaunchCommand => string.Empty; |
|
public override Uri PreviewImageUri => |
|
new( |
|
"https://raw.githubusercontent.com/Stability-AI/StableSwarmUI/master/.github/images/stableswarmui.jpg" |
|
); |
|
public override string OutputFolderName => "Output"; |
|
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods => |
|
[SharedFolderMethod.Symlink, SharedFolderMethod.Configuration, SharedFolderMethod.None]; |
|
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Configuration; |
|
public override bool OfferInOneClickInstaller => false; |
|
|
|
public override List<LaunchOptionDefinition> LaunchOptions => |
|
[ |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Host", |
|
Type = LaunchOptionType.String, |
|
DefaultValue = "127.0.0.1", |
|
Options = ["--host"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Port", |
|
Type = LaunchOptionType.String, |
|
DefaultValue = "7801", |
|
Options = ["--port"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Ngrok Path", |
|
Type = LaunchOptionType.String, |
|
Options = ["--ngrok-path"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Ngrok Basic Auth", |
|
Type = LaunchOptionType.String, |
|
Options = ["--ngrok-basic-auth"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Cloudflared Path", |
|
Type = LaunchOptionType.String, |
|
Options = ["--cloudflared-path"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Proxy Region", |
|
Type = LaunchOptionType.String, |
|
Options = ["--proxy-region"] |
|
}, |
|
new LaunchOptionDefinition |
|
{ |
|
Name = "Launch Mode", |
|
Type = LaunchOptionType.Bool, |
|
Options = ["--launch-mode web", "--launch-mode webinstall"] |
|
}, |
|
LaunchOptionDefinition.Extras |
|
]; |
|
|
|
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders => |
|
new() |
|
{ |
|
[SharedFolderType.StableDiffusion] = ["Models/Stable-Diffusion"], |
|
[SharedFolderType.Lora] = ["Models/Lora"], |
|
[SharedFolderType.VAE] = ["Models/VAE"], |
|
[SharedFolderType.TextualInversion] = ["Models/Embeddings"], |
|
[SharedFolderType.ControlNet] = ["Models/controlnet"], |
|
[SharedFolderType.InvokeClipVision] = ["Models/clip_vision"] |
|
}; |
|
public override Dictionary<SharedOutputType, IReadOnlyList<string>> SharedOutputFolders => |
|
new() { [SharedOutputType.Text2Img] = [OutputFolderName] }; |
|
public override string MainBranch => "master"; |
|
public override bool ShouldIgnoreReleases => true; |
|
public override IEnumerable<TorchVersion> AvailableTorchVersions => |
|
[TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm, TorchVersion.Mps]; |
|
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Advanced; |
|
public override IEnumerable<PackagePrerequisite> Prerequisites => |
|
[ |
|
PackagePrerequisite.Git, |
|
PackagePrerequisite.Dotnet, |
|
PackagePrerequisite.Python310, |
|
PackagePrerequisite.VcRedist |
|
]; |
|
|
|
private FilePath GetSettingsPath(string installLocation) => |
|
Path.Combine(installLocation, "Data", "Settings.fds"); |
|
|
|
private FilePath GetBackendsPath(string installLocation) => |
|
Path.Combine(installLocation, "Data", "Backends.fds"); |
|
|
|
public override async Task InstallPackage( |
|
string installLocation, |
|
TorchVersion torchVersion, |
|
SharedFolderMethod selectedSharedFolderMethod, |
|
DownloadPackageVersionOptions versionOptions, |
|
IProgress<ProgressReport>? progress = null, |
|
Action<ProcessOutput>? onConsoleOutput = null |
|
) |
|
{ |
|
progress?.Report(new ProgressReport(-1f, "Installing StableSwarmUI...", isIndeterminate: true)); |
|
|
|
var comfy = settingsManager.Settings.InstalledPackages.FirstOrDefault( |
|
x => x.PackageName == nameof(ComfyUI) |
|
); |
|
|
|
if (comfy == null) |
|
{ |
|
throw new InvalidOperationException("ComfyUI must be installed to use StableSwarmUI"); |
|
} |
|
|
|
try |
|
{ |
|
await prerequisiteHelper |
|
.RunDotnet( |
|
[ |
|
"nuget", |
|
"add", |
|
"source", |
|
"https://api.nuget.org/v3/index.json", |
|
"--name", |
|
"\"NuGet official package source\"" |
|
], |
|
workingDirectory: installLocation, |
|
onProcessOutput: onConsoleOutput |
|
) |
|
.ConfigureAwait(false); |
|
} |
|
catch (ProcessException e) |
|
{ |
|
// ignore, probably means the source is already there |
|
} |
|
|
|
await prerequisiteHelper |
|
.RunDotnet( |
|
[ |
|
"build", |
|
"src/StableSwarmUI.csproj", |
|
"--configuration", |
|
"Release", |
|
"-o", |
|
"src/bin/live_release" |
|
], |
|
workingDirectory: installLocation, |
|
onProcessOutput: onConsoleOutput |
|
) |
|
.ConfigureAwait(false); |
|
|
|
// set default settings |
|
var settings = new StableSwarmSettings { IsInstalled = true }; |
|
|
|
if (selectedSharedFolderMethod is SharedFolderMethod.Configuration) |
|
{ |
|
settings.Paths = new StableSwarmSettings.PathsData |
|
{ |
|
ModelRoot = settingsManager.ModelsDirectory, |
|
SDModelFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.StableDiffusion.ToString() |
|
), |
|
SDLoraFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.Lora.ToString() |
|
), |
|
SDVAEFolder = Path.Combine(settingsManager.ModelsDirectory, SharedFolderType.VAE.ToString()), |
|
SDEmbeddingFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.TextualInversion.ToString() |
|
), |
|
SDControlNetsFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.ControlNet.ToString() |
|
), |
|
SDClipVisionFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.InvokeClipVision.ToString() |
|
) |
|
}; |
|
} |
|
|
|
settings.Save(true).SaveToFile(GetSettingsPath(installLocation)); |
|
|
|
var backendsFile = new FDSSection(); |
|
var dataSection = new FDSSection(); |
|
dataSection.Set("type", "comfyui_selfstart"); |
|
dataSection.Set("title", "StabilityMatrix ComfyUI Self-Start"); |
|
dataSection.Set("enabled", true); |
|
|
|
var launchArgs = comfy.LaunchArgs ?? []; |
|
var comfyArgs = string.Join( |
|
' ', |
|
launchArgs |
|
.Select(arg => arg.ToArgString()?.TrimEnd()) |
|
.Where(arg => !string.IsNullOrWhiteSpace(arg)) |
|
); |
|
|
|
dataSection.Set( |
|
"settings", |
|
new ComfyUiSelfStartSettings |
|
{ |
|
StartScript = $"../{comfy.DisplayName}/main.py", |
|
DisableInternalArgs = false, |
|
AutoUpdate = false, |
|
ExtraArgs = comfyArgs |
|
}.Save(true) |
|
); |
|
|
|
backendsFile.Set("0", dataSection); |
|
backendsFile.SaveToFile(GetBackendsPath(installLocation)); |
|
} |
|
|
|
public override async Task RunPackage( |
|
string installedPackagePath, |
|
string command, |
|
string arguments, |
|
Action<ProcessOutput>? onConsoleOutput |
|
) |
|
{ |
|
var aspEnvVars = new Dictionary<string, string> |
|
{ |
|
["ASPNETCORE_ENVIRONMENT"] = "Production", |
|
["ASPNETCORE_URLS"] = "http://*:7801" |
|
}; |
|
|
|
void HandleConsoleOutput(ProcessOutput s) |
|
{ |
|
onConsoleOutput?.Invoke(s); |
|
|
|
if (s.Text.Contains("Starting webserver", StringComparison.OrdinalIgnoreCase)) |
|
{ |
|
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)"); |
|
var match = regex.Match(s.Text); |
|
if (match.Success) |
|
{ |
|
WebUrl = match.Value; |
|
} |
|
OnStartupComplete(WebUrl); |
|
} |
|
} |
|
|
|
dotnetProcess = await prerequisiteHelper |
|
.RunDotnet( |
|
args: $"src{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}live_release{Path.DirectorySeparatorChar}StableSwarmUI.dll {arguments.TrimEnd()}", |
|
workingDirectory: installedPackagePath, |
|
envVars: aspEnvVars, |
|
onProcessOutput: HandleConsoleOutput, |
|
waitForExit: false |
|
) |
|
.ConfigureAwait(false); |
|
} |
|
|
|
public override Task SetupModelFolders( |
|
DirectoryPath installDirectory, |
|
SharedFolderMethod sharedFolderMethod |
|
) => |
|
sharedFolderMethod switch |
|
{ |
|
SharedFolderMethod.Symlink |
|
=> base.SetupModelFolders(installDirectory, SharedFolderMethod.Symlink), |
|
SharedFolderMethod.Configuration => SetupModelFoldersConfig(installDirectory), // TODO |
|
_ => Task.CompletedTask |
|
}; |
|
|
|
public override Task RemoveModelFolderLinks( |
|
DirectoryPath installDirectory, |
|
SharedFolderMethod sharedFolderMethod |
|
) => |
|
sharedFolderMethod switch |
|
{ |
|
SharedFolderMethod.Symlink => base.RemoveModelFolderLinks(installDirectory, sharedFolderMethod), |
|
SharedFolderMethod.Configuration => RemoveModelFoldersConfig(installDirectory), |
|
_ => Task.CompletedTask |
|
}; |
|
|
|
public override async Task WaitForShutdown() |
|
{ |
|
if (dotnetProcess is { HasExited: false }) |
|
{ |
|
dotnetProcess.Kill(true); |
|
try |
|
{ |
|
await dotnetProcess |
|
.WaitForExitAsync(new CancellationTokenSource(5000).Token) |
|
.ConfigureAwait(false); |
|
} |
|
catch (OperationCanceledException e) |
|
{ |
|
Console.WriteLine(e); |
|
} |
|
} |
|
|
|
dotnetProcess = null; |
|
GC.SuppressFinalize(this); |
|
} |
|
|
|
private Task SetupModelFoldersConfig(DirectoryPath installDirectory) |
|
{ |
|
var settingsPath = GetSettingsPath(installDirectory); |
|
var existingSettings = new StableSwarmSettings(); |
|
var settingsExists = File.Exists(settingsPath); |
|
if (settingsExists) |
|
{ |
|
var section = FDSUtility.ReadFile(settingsPath); |
|
existingSettings.Load(section); |
|
} |
|
|
|
existingSettings.Paths = new StableSwarmSettings.PathsData |
|
{ |
|
ModelRoot = settingsManager.ModelsDirectory, |
|
SDModelFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.StableDiffusion.ToString() |
|
), |
|
SDLoraFolder = Path.Combine(settingsManager.ModelsDirectory, SharedFolderType.Lora.ToString()), |
|
SDVAEFolder = Path.Combine(settingsManager.ModelsDirectory, SharedFolderType.VAE.ToString()), |
|
SDEmbeddingFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.TextualInversion.ToString() |
|
), |
|
SDControlNetsFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.ControlNet.ToString() |
|
), |
|
SDClipVisionFolder = Path.Combine( |
|
settingsManager.ModelsDirectory, |
|
SharedFolderType.InvokeClipVision.ToString() |
|
) |
|
}; |
|
|
|
existingSettings.Save(true).SaveToFile(settingsPath); |
|
|
|
return Task.CompletedTask; |
|
} |
|
|
|
private Task RemoveModelFoldersConfig(DirectoryPath installDirectory) |
|
{ |
|
var settingsPath = GetSettingsPath(installDirectory); |
|
var existingSettings = new StableSwarmSettings(); |
|
var settingsExists = File.Exists(settingsPath); |
|
if (settingsExists) |
|
{ |
|
var section = FDSUtility.ReadFile(settingsPath); |
|
existingSettings.Load(section); |
|
} |
|
|
|
existingSettings.Paths = new StableSwarmSettings.PathsData(); |
|
existingSettings.Save(true).SaveToFile(settingsPath); |
|
|
|
return Task.CompletedTask; |
|
} |
|
}
|
|
|