Browse Source

Added HuggingFace page for HF model downloads (still WIP) & updated Packages to use the new .net 8 constructor feature thing

pull/324/head
JT 12 months ago
parent
commit
d0121ca026
  1. 3
      StabilityMatrix.Avalonia/App.axaml.cs
  2. 3
      StabilityMatrix.Avalonia/Assets.cs
  3. 335
      StabilityMatrix.Avalonia/Assets/hf-packages.json
  4. 2
      StabilityMatrix.Avalonia/Controls/StackExpander.axaml
  5. 4
      StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs
  6. 3
      StabilityMatrix.Avalonia/DesignData/DesignData.cs
  7. 9
      StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
  8. 3
      StabilityMatrix.Avalonia/Languages/Resources.resx
  9. 38
      StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs
  10. 12
      StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs
  11. 1
      StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj
  12. 53
      StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs
  13. 25
      StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs
  14. 192
      StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs
  15. 184
      StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml
  16. 13
      StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml.cs
  17. 187
      StabilityMatrix.Core/Models/Packages/A3WebUI.cs
  18. 197
      StabilityMatrix.Core/Models/Packages/ComfyUI.cs
  19. 15
      StabilityMatrix.Core/Models/Packages/FocusControlNet.cs
  20. 15
      StabilityMatrix.Core/Models/Packages/Fooocus.cs
  21. 15
      StabilityMatrix.Core/Models/Packages/FooocusMre.cs
  22. 109
      StabilityMatrix.Core/Models/Packages/InvokeAI.cs
  23. 135
      StabilityMatrix.Core/Models/Packages/KohyaSs.cs
  24. 20
      StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs
  25. 15
      StabilityMatrix.Core/Models/Packages/StableDiffusionDirectMl.cs
  26. 183
      StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs
  27. 173
      StabilityMatrix.Core/Models/Packages/VladAutomatic.cs
  28. 15
      StabilityMatrix.Core/Models/Packages/VoltaML.cs
  29. 4
      StabilityMatrix.Core/Models/SharedFolderType.cs

3
StabilityMatrix.Avalonia/App.axaml.cs

@ -291,7 +291,8 @@ public sealed class App : Application
provider.GetRequiredService<PackageManagerViewModel>(),
provider.GetRequiredService<CheckpointsPageViewModel>(),
provider.GetRequiredService<CheckpointBrowserViewModel>(),
provider.GetRequiredService<OutputsPageViewModel>()
provider.GetRequiredService<OutputsPageViewModel>(),
provider.GetRequiredService<HuggingFacePageViewModel>()
},
FooterPages = { provider.GetRequiredService<SettingsViewModel>() }
}

3
StabilityMatrix.Avalonia/Assets.cs

@ -32,6 +32,9 @@ internal static class Assets
public static AvaloniaResource ThemeMatrixDarkJson =>
new("avares://StabilityMatrix.Avalonia/Assets/ThemeMatrixDark.json");
public static AvaloniaResource HfPackagesJson =>
new("avares://StabilityMatrix.Avalonia/Assets/hf-packages.json");
private const UnixFileMode Unix755 =
UnixFileMode.UserRead
| UnixFileMode.UserWrite

335
StabilityMatrix.Avalonia/Assets/hf-packages.json

@ -0,0 +1,335 @@
[
{
"ModelCategory": "BaseModel",
"ModelName": "Stable Diffusion 1.5",
"RepositoryPath": "runwayml/stable-diffusion-v1-5",
"Files": [
"v1-5-pruned-emaonly.safetensors"
],
"LicenseType": "CreativeML Open RAIL-M"
},
{
"ModelCategory": "BaseModel",
"ModelName": "Stable Diffusion 2.1",
"RepositoryPath": "stabilityai/stable-diffusion-2-1",
"Files": [
"v2-1_768-ema-pruned.safetensors"
],
"LicenseType": "Open RAIL++"
},
{
"ModelCategory": "BaseModel",
"ModelName": "Stable Diffusion XL (Base)",
"RepositoryPath": "stabilityai/stable-diffusion-xl-base-1.0",
"Files": [
"sd_xl_base_1.0_0.9vae.safetensors",
"sd_xl_offset_example-lora_1.0.safetensors"
],
"LicenseType": "Open RAIL++",
"LicensePath": "LICENSE.md"
},
{
"ModelCategory": "BaseModel",
"ModelName": "Stable Diffusion XL (Refiner)",
"RepositoryPath": "stabilityai/stable-diffusion-xl-refiner-1.0",
"Files": [
"sd_xl_refiner_1.0_0.9vae.safetensors"
],
"LicenseType": "Open RAIL++",
"LicensePath": "LICENSE.md"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Canny",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_canny.pth",
"control_v11p_sd15_canny.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Depth",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11f1p_sd15_depth.pth",
"control_v11f1p_sd15_depth.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "MLSD",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_mlsd.pth",
"control_v11p_sd15_mlsd.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Inpaint",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_inpaint.pth",
"control_v11p_sd15_inpaint.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "IP2P",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11e_sd15_ip2p.pth",
"control_v11e_sd15_ip2p.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Tile",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11f1e_sd15_tile.pth",
"control_v11f1e_sd15_tile.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "OpenPose",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_openpose.pth",
"control_v11p_sd15_openpose.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "LineArt",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_lineart.pth",
"control_v11p_sd15_lineart.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "LineArt Anime",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15s2_lineart_anime.pth",
"control_v11p_sd15s2_lineart_anime.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "NormalBae",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_normalbae.pth",
"control_v11p_sd15_normalbae.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Seg",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_seg.pth",
"control_v11p_sd15_seg.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Soft Edge",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_softedge.pth",
"control_v11p_sd15_softedge.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Scribble",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11p_sd15_scribble.pth",
"control_v11p_sd15_scribble.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "ControlNet",
"ModelName": "Shuffle",
"RepositoryPath": "lllyasviel/ControlNet-v1-1",
"Files": [
"control_v11e_sd15_shuffle.pth",
"control_v11e_sd15_shuffle.yaml"
],
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Canny",
"RepositoryPath": "lllyasviel/control_v11p_sd15_canny",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "canny",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Depth",
"RepositoryPath": "lllyasviel/control_v11f1p_sd15_depth",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "depth",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "MLSD",
"RepositoryPath": "lllyasviel/control_v11p_sd15_mlsd",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "MLSD",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Inpaint",
"RepositoryPath": "lllyasviel/control_v11p_sd15_inpaint",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "inpaint",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "IP2P",
"RepositoryPath": "lllyasviel/control_v11e_sd15_ip2p",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "ip2p",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Tile",
"RepositoryPath": "lllyasviel/control_v11f1e_sd15_tile",
"Files": [
"diffusion_pytorch_model.bin",
"config.json"
],
"Subfolder": "tile",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "OpenPose",
"RepositoryPath": "lllyasviel/control_v11p_sd15_openpose",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "openpose",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "LineArt",
"RepositoryPath": "lllyasviel/control_v11p_sd15_lineart",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "lineart",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "LineArt Anime",
"RepositoryPath": "lllyasviel/control_v11p_sd15s2_lineart_anime",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "lineart_anime",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "NormalBae",
"RepositoryPath": "lllyasviel/control_v11p_sd15_normalbae",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "normalbae",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Seg",
"RepositoryPath": "lllyasviel/control_v11p_sd15_seg",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "seg",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Soft Edge",
"RepositoryPath": "lllyasviel/control_v11p_sd15_softedge",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "softedge",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Scribble",
"RepositoryPath": "lllyasviel/control_v11p_sd15_scribble",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "scribble",
"LicenseType": "Open RAIL"
},
{
"ModelCategory": "DiffusersControlNet",
"ModelName": "Shuffle",
"RepositoryPath": "lllyasviel/control_v11e_sd15_shuffle",
"Files": [
"diffusion_pytorch_model.safetensors",
"config.json"
],
"Subfolder": "shuffle",
"LicenseType": "Open RAIL"
}
]

2
StabilityMatrix.Avalonia/Controls/StackExpander.axaml

@ -21,6 +21,8 @@
<ControlTemplate>
<Expander
VerticalContentAlignment="Top"
ExpandDirection="{TemplateBinding ExpandDirection}"
IsExpanded="{TemplateBinding IsExpanded}"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
VerticalAlignment="{TemplateBinding VerticalAlignment}">

4
StabilityMatrix.Avalonia/Controls/StackExpander.axaml.cs

@ -1,11 +1,11 @@
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Controls;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.Controls;
[Transient]
public class StackExpander : TemplatedControl
public class StackExpander : Expander
{
public static readonly StyledProperty<int> SpacingProperty = AvaloniaProperty.Register<
StackCard,

3
StabilityMatrix.Avalonia/DesignData/DesignData.cs

@ -426,6 +426,9 @@ public static class DesignData
public static LaunchPageViewModel LaunchPageViewModel =>
Services.GetRequiredService<LaunchPageViewModel>();
public static HuggingFacePageViewModel HuggingFacePageViewModel =>
Services.GetRequiredService<HuggingFacePageViewModel>();
public static OutputsPageViewModel OutputsPageViewModel
{

9
StabilityMatrix.Avalonia/Languages/Resources.Designer.cs generated

@ -338,6 +338,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Open on Hugging Face.
/// </summary>
public static string Action_OpenOnHuggingFace {
get {
return ResourceManager.GetString("Action_OpenOnHuggingFace", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Open Project....
/// </summary>

3
StabilityMatrix.Avalonia/Languages/Resources.resx

@ -813,4 +813,7 @@
<data name="TeachingTip_MoreCheckpointCategories" xml:space="preserve">
<value>Additional folders such as IPAdapters and TextualInversions (embeddings) can be enabled here</value>
</data>
<data name="Action_OpenOnHuggingFace" xml:space="preserve">
<value>Open on Hugging Face</value>
</data>
</root>

38
StabilityMatrix.Avalonia/Models/HuggingFace/HuggingFaceModelType.cs

@ -0,0 +1,38 @@
using System.Text.Json.Serialization;
using StabilityMatrix.Core.Converters.Json;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Models;
namespace StabilityMatrix.Avalonia.Models.HuggingFace;
[JsonConverter(typeof(DefaultUnknownEnumConverter<HuggingFaceModelType>))]
public enum HuggingFaceModelType
{
[Description("Base Models")]
[ConvertTo<SharedFolderType>(SharedFolderType.StableDiffusion)]
BaseModel,
[Description("ControlNets")]
[ConvertTo<SharedFolderType>(SharedFolderType.ControlNet)]
ControlNet,
[Description("ControlNets (Diffusers)")]
[ConvertTo<SharedFolderType>(SharedFolderType.ControlNet)]
DiffusersControlNet,
[Description("IP Adapters")]
[ConvertTo<SharedFolderType>(SharedFolderType.IpAdapter)]
IpAdapter,
[Description("IP Adapters (Diffusers)")]
[ConvertTo<SharedFolderType>(SharedFolderType.IpAdapter)]
DiffusersIpAdapter,
[Description("T2I Adapters")]
[ConvertTo<SharedFolderType>(SharedFolderType.T2IAdapter)]
T2IAdapter,
[Description("T2I Adapters (Diffusers)")]
[ConvertTo<SharedFolderType>(SharedFolderType.T2IAdapter)]
DiffusersT2IAdapter,
}

12
StabilityMatrix.Avalonia/Models/HuggingFace/HuggingfaceItem.cs

@ -0,0 +1,12 @@
namespace StabilityMatrix.Avalonia.Models.HuggingFace;
public class HuggingfaceItem
{
public required HuggingFaceModelType ModelCategory { get; set; }
public required string ModelName { get; set; }
public required string RepositoryPath { get; set; }
public required string[] Files { get; set; }
public required string LicenseType { get; set; }
public string? LicensePath { get; set; }
public string? Subfolder { get; set; }
}

1
StabilityMatrix.Avalonia/StabilityMatrix.Avalonia.csproj

@ -79,6 +79,7 @@
<ItemGroup>
<AvaloniaResource Include="Assets\Icon.ico" />
<AvaloniaResource Include="Assets\Icon.png" />
<AvaloniaResource Include="Assets\hf-packages.json" />
</ItemGroup>
<ItemGroup>

53
StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/CategoryViewModel.cs

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using DynamicData.Binding;
using StabilityMatrix.Avalonia.Models.HuggingFace;
using StabilityMatrix.Avalonia.ViewModels.Base;
namespace StabilityMatrix.Avalonia.ViewModels.HuggingFacePage;
public partial class CategoryViewModel : ViewModelBase
{
[ObservableProperty]
private IObservableCollection<HuggingfaceItemViewModel> items =
new ObservableCollectionExtended<HuggingfaceItemViewModel>();
public SourceCache<HuggingfaceItem, string> ItemsCache { get; } =
new(i => i.RepositoryPath + i.ModelName);
[ObservableProperty]
private string? title;
[ObservableProperty]
private bool isChecked;
[ObservableProperty]
private int numSelected;
public CategoryViewModel(IEnumerable<HuggingfaceItem> items)
{
ItemsCache
.Connect()
.DeferUntilLoaded()
.Transform(i => new HuggingfaceItemViewModel { Item = i })
.Bind(Items)
.WhenPropertyChanged(p => p.IsSelected)
.Subscribe(_ => NumSelected = Items.Count(i => i.IsSelected));
ItemsCache.EditDiff(items, (a, b) => a.RepositoryPath == b.RepositoryPath);
}
partial void OnIsCheckedChanged(bool value)
{
if (Items is null)
return;
foreach (var item in Items)
{
item.IsSelected = value;
}
}
}

25
StabilityMatrix.Avalonia/ViewModels/HuggingFacePage/HuggingfaceItemViewModel.cs

@ -0,0 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using StabilityMatrix.Avalonia.Models.HuggingFace;
using StabilityMatrix.Avalonia.ViewModels.Base;
namespace StabilityMatrix.Avalonia.ViewModels.HuggingFacePage;
public partial class HuggingfaceItemViewModel : ViewModelBase
{
[ObservableProperty]
private HuggingfaceItem item;
[ObservableProperty]
private bool isSelected;
public string LicenseUrl =>
$"https://huggingface.co/{Item.RepositoryPath}/blob/main/{Item.LicensePath ?? "README.md"}";
public string RepoUrl => $"https://huggingface.co/{Item.RepositoryPath}";
[RelayCommand]
private void ToggleSelected()
{
IsSelected = !IsSelected;
}
}

192
StabilityMatrix.Avalonia/ViewModels/HuggingFacePageViewModel.cs

@ -0,0 +1,192 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
using DynamicData.Binding;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Models.HuggingFace;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.HuggingFacePage;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(Views.HuggingFacePage))]
[Singleton]
public partial class HuggingFacePageViewModel : PageViewModelBase
{
private readonly ITrackedDownloadService trackedDownloadService;
private readonly ISettingsManager settingsManager;
private readonly INotificationService notificationService;
public override string Title => "Hugging Face";
public override IconSource IconSource =>
new FASymbolIconSource { Symbol = "fa-solid fa-file-code" };
public SourceCache<HuggingfaceItem, string> ItemsCache { get; } =
new(i => i.RepositoryPath + i.ModelName);
public IObservableCollection<CategoryViewModel> Categories { get; set; } =
new ObservableCollectionExtended<CategoryViewModel>();
public string DownloadPercentText =>
Math.Abs(TotalProgress.Percentage - 100f) < 0.001f
? "Download Complete"
: $"Downloading {TotalProgress.Percentage:0}%";
[ObservableProperty]
private int numSelected;
private ConcurrentDictionary<Guid, ProgressReport> progressReports = new();
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(DownloadPercentText))]
private ProgressReport totalProgress;
private readonly DispatcherTimer progressTimer =
new() { Interval = TimeSpan.FromMilliseconds(100) };
public HuggingFacePageViewModel(
ITrackedDownloadService trackedDownloadService,
ISettingsManager settingsManager,
INotificationService notificationService
)
{
this.trackedDownloadService = trackedDownloadService;
this.settingsManager = settingsManager;
this.notificationService = notificationService;
ItemsCache
.Connect()
.DeferUntilLoaded()
.Group(i => i.ModelCategory)
.Transform(
g =>
new CategoryViewModel(g.Cache.Items)
{
Title = g.Key.GetDescription() ?? g.Key.ToString()
}
)
.SortBy(vm => vm.Title)
.Bind(Categories)
.WhenAnyPropertyChanged()
.Subscribe(_ => NumSelected = Categories.Sum(c => c.NumSelected));
progressTimer.Tick += (_, _) =>
{
var currentSum = 0ul;
var totalSum = 0ul;
foreach (var progress in progressReports.Values)
{
currentSum += progress.Current ?? 0;
totalSum += progress.Total ?? 0;
}
TotalProgress = new ProgressReport(current: currentSum, total: totalSum);
};
}
public override void OnLoaded()
{
using var reader = new StreamReader(Assets.HfPackagesJson.Open());
var packages =
JsonSerializer.Deserialize<IReadOnlyList<HuggingfaceItem>>(
reader.ReadToEnd(),
new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } }
) ?? throw new InvalidOperationException("Failed to read hf-packages.json");
ItemsCache.EditDiff(packages, (a, b) => a.RepositoryPath == b.RepositoryPath);
}
public void ClearSelection()
{
foreach (var category in Categories)
{
category.IsChecked = true;
category.IsChecked = false;
}
}
public void SelectAll()
{
foreach (var category in Categories)
{
category.IsChecked = true;
}
}
public async Task ImportSelected()
{
var selected = Categories.SelectMany(c => c.Items).Where(i => i.IsSelected).ToArray();
foreach (var viewModel in selected)
{
foreach (var file in viewModel.Item.Files)
{
var url =
$"https://huggingface.co/{viewModel.Item.RepositoryPath}/resolve/main/{file}?download=true";
var sharedFolderType = viewModel.Item.ModelCategory.ConvertTo<SharedFolderType>();
var downloadPath = new FilePath(
Path.Combine(
settingsManager.ModelsDirectory,
sharedFolderType.GetDescription() ?? sharedFolderType.ToString(),
viewModel.Item.Subfolder ?? string.Empty,
file
)
);
Directory.CreateDirectory(downloadPath.Directory);
var download = trackedDownloadService.NewDownload(url, downloadPath);
download.ProgressUpdate += DownloadOnProgressUpdate;
download.Start();
await Task.Delay(Random.Shared.Next(50, 100));
}
}
progressTimer.Start();
}
private void DownloadOnProgressUpdate(object? sender, ProgressReport e)
{
if (sender is not TrackedDownload trackedDownload)
return;
progressReports[trackedDownload.Id] = e;
}
partial void OnTotalProgressChanged(ProgressReport value)
{
if (Math.Abs(value.Percentage - 100) < 0.001f)
{
notificationService.Show(
"Download complete",
"All selected models have been downloaded.",
NotificationType.Success
);
progressTimer.Stop();
DelayedClearProgress(TimeSpan.FromSeconds(1.5));
}
}
private void DelayedClearProgress(TimeSpan delay)
{
Task.Delay(delay)
.ContinueWith(_ =>
{
TotalProgress = new ProgressReport(0, 0);
});
}
}

184
StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml

@ -0,0 +1,184 @@
<controls:UserControlBase
x:Class="StabilityMatrix.Avalonia.Views.HuggingFacePage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:StabilityMatrix.Avalonia.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lang="clr-namespace:StabilityMatrix.Avalonia.Languages"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mocks="clr-namespace:StabilityMatrix.Avalonia.DesignData"
xmlns:ui="using:FluentAvalonia.UI.Controls"
xmlns:vm="clr-namespace:StabilityMatrix.Avalonia.ViewModels"
xmlns:huggingFacePage="clr-namespace:StabilityMatrix.Avalonia.ViewModels.HuggingFacePage"
xmlns:huggingFace="clr-namespace:StabilityMatrix.Avalonia.Models.HuggingFace"
xmlns:helpers="clr-namespace:StabilityMatrix.Avalonia.Helpers"
d:DataContext="{x:Static mocks:DesignData.HuggingFacePageViewModel}"
d:DesignHeight="650"
d:DesignWidth="800"
x:CompileBindings="True"
x:DataType="vm:HuggingFacePageViewModel"
Focusable="True"
mc:Ignorable="d">
<ScrollViewer>
<Grid RowDefinitions="Auto,Auto,Auto,Auto" ColumnDefinitions="Auto, *" Margin="8">
<TextBlock VerticalAlignment="Center"
IsVisible="{Binding !!NumSelected}"
Margin="8,0,0,0">
<Run Text="{Binding NumSelected}" />
<Run Text="models selected" />
</TextBlock>
<ui:CommandBar
Grid.Row="0"
Grid.Column="1"
Margin="8,0,0,0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
VerticalContentAlignment="Center"
DefaultLabelPosition="Right">
<ui:CommandBar.PrimaryCommands>
<ui:CommandBarButton
IsEnabled="{Binding !!NumSelected}"
IconSource="Download"
Foreground="{DynamicResource AccentButtonBackground}"
Command="{Binding ImportSelected}"
Label="Import Selected" />
<ui:CommandBarSeparator />
<ui:CommandBarButton
Command="{Binding SelectAll}"
IconSource="SelectAll"
Label="{x:Static lang:Resources.Action_SelectAll}" />
<ui:CommandBarButton
IconSource="ClearSelection"
Command="{Binding ClearSelection}"
IsEnabled="{Binding !!NumSelected}"
Label="{x:Static lang:Resources.Action_ClearSelection}" />
</ui:CommandBar.PrimaryCommands>
</ui:CommandBar>
<StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Orientation="Vertical"
Margin="0,8"
VerticalAlignment="Bottom">
<TextBlock Text="{Binding DownloadPercentText}"
TextAlignment="Center"
IsVisible="{Binding !!TotalProgress.Total}"
Margin="0,4"
HorizontalAlignment="Stretch" />
<ProgressBar Value="{Binding TotalProgress.Current}"
Maximum="{Binding TotalProgress.Total}"
IsVisible="{Binding !!TotalProgress.Total}"
IsIndeterminate="False" />
</StackPanel>
<ItemsRepeater Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2"
ItemsSource="{Binding Categories}">
<ItemsRepeater.Layout>
<StackLayout Orientation="Vertical" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type huggingFacePage:CategoryViewModel}">
<Expander IsExpanded="True" Margin="0,0,0,8">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Title}" />
</StackPanel>
</Expander.Header>
<ItemsRepeater ItemsSource="{Binding Items}"
Margin="8,0">
<ItemsRepeater.Layout>
<UniformGridLayout MinColumnSpacing="8"
MinRowSpacing="8"
MinItemWidth="220"
ItemsStretch="Uniform" />
</ItemsRepeater.Layout>
<ItemsRepeater.ItemTemplate>
<DataTemplate DataType="{x:Type huggingFacePage:HuggingfaceItemViewModel}">
<Button
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Command="{Binding ToggleSelectedCommand}"
CornerRadius="8">
<Button.ContextFlyout>
<MenuFlyout>
<MenuItem Command="{x:Static helpers:IOCommands.OpenUrlCommand}"
CommandParameter="{Binding RepoUrl}"
Header="{x:Static lang:Resources.Action_OpenOnHuggingFace}">
<MenuItem.Icon>
<ui:SymbolIcon Symbol="Open" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static lang:Resources.Label_License}"
Command="{x:Static helpers:IOCommands.OpenUrlCommand}"
CommandParameter="{Binding LicenseUrl}">
<MenuItem.Icon>
<ui:SymbolIcon Symbol="Document" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Button.ContextFlyout>
<Grid ColumnDefinitions="Auto, *">
<CheckBox
VerticalAlignment="Center"
IsChecked="{Binding IsSelected}"
Content="{Binding Item.ModelName}" />
<Button
Grid.Column="1"
Width="24"
Margin="0,4,4,0"
Padding="4"
HorizontalAlignment="Right"
VerticalAlignment="Top"
HorizontalContentAlignment="Right"
VerticalContentAlignment="Top"
BorderThickness="0"
Classes="transparent">
<ui:SymbolIcon FontSize="18" Symbol="MoreVertical" />
<Button.Flyout>
<MenuFlyout>
<MenuItem Command="{x:Static helpers:IOCommands.OpenUrlCommand}"
CommandParameter="{Binding RepoUrl}"
Header="{x:Static lang:Resources.Action_OpenOnHuggingFace}">
<MenuItem.Icon>
<ui:SymbolIcon Symbol="Open" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{x:Static lang:Resources.Label_License}"
Command="{x:Static helpers:IOCommands.OpenUrlCommand}"
CommandParameter="{Binding LicenseUrl}">
<MenuItem.Icon>
<ui:SymbolIcon Symbol="List" />
</MenuItem.Icon>
</MenuItem>
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</Button>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
</Expander>
</DataTemplate>
</ItemsRepeater.ItemTemplate>
</ItemsRepeater>
<Expander Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="3"
Header="Manual Model Download">
<Grid RowDefinitions="Auto, Auto" ColumnDefinitions="*, Auto">
<TextBox Grid.Row="1" Grid.Column="0"
Watermark="Enter HuggingFace repo URL"
Margin="0,8" />
<Button Grid.Row="1" Grid.Column="1"
Margin="8,8,0,8"
Classes="accent"
Content="Import" />
</Grid>
</Expander>
</Grid>
</ScrollViewer>
</controls:UserControlBase>

13
StabilityMatrix.Avalonia/Views/HuggingFacePage.axaml.cs

@ -0,0 +1,13 @@
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Core.Attributes;
namespace StabilityMatrix.Avalonia.Views;
[Singleton]
public partial class HuggingFacePage : UserControlBase
{
public HuggingFacePage()
{
InitializeComponent();
}
}

187
StabilityMatrix.Core/Models/Packages/A3WebUI.cs

@ -14,7 +14,12 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class A3WebUI : BaseGitPackage
public class A3WebUI(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper)
: BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -35,14 +40,6 @@ public class A3WebUI : BaseGitPackage
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink;
public A3WebUI(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
// From https://github.com/AUTOMATIC1111/stable-diffusion-webui/tree/master/models
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
@ -62,7 +59,9 @@ public class A3WebUI : BaseGitPackage
[SharedFolderType.ControlNet] = new[] { "models/ControlNet" },
[SharedFolderType.Codeformer] = new[] { "models/Codeformer" },
[SharedFolderType.LDSR] = new[] { "models/LDSR" },
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" }
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" }
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
@ -78,27 +77,28 @@ public class A3WebUI : BaseGitPackage
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
public override List<LaunchOptionDefinition> LaunchOptions =>
[
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = ["--server-name"]
},
new()
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = new() { "--server-name" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = new() { "--port" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = ["--port"]
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
@ -107,66 +107,75 @@ public class A3WebUI : BaseGitPackage
Level.Medium => "--medvram",
_ => null
},
Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" }
},
new()
{
Name = "Xformers",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = new() { "--xformers" }
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--api" }
},
new()
{
Name = "Auto Launch Web UI",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--autolaunch" }
},
new()
{
Name = "Skip Torch CUDA Check",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = new() { "--skip-torch-cuda-test" }
},
new()
{
Name = "Skip Python Version Check",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--skip-python-version-check" }
},
new()
{
Name = "No Half",
Type = LaunchOptionType.Bool,
Description = "Do not switch the model to 16-bit floats",
InitialValue = HardwareHelper.PreferRocm() || HardwareHelper.PreferDirectML(),
Options = new() { "--no-half" }
},
new()
{
Name = "Skip SD Model Download",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--no-download-sd-model" }
},
new()
{
Name = "Skip Install",
Type = LaunchOptionType.Bool,
Options = new() { "--skip-install" }
},
LaunchOptionDefinition.Extras
};
Options = ["--lowvram", "--medvram", "--medvram-sdxl"]
},
new()
{
Name = "Xformers",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = ["--xformers"]
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = ["--api"]
},
new()
{
Name = "Auto Launch Web UI",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = ["--autolaunch"]
},
new()
{
Name = "Skip Torch CUDA Check",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = ["--skip-torch-cuda-test"]
},
new()
{
Name = "Skip Python Version Check",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = ["--skip-python-version-check"]
},
new()
{
Name = "No Half",
Type = LaunchOptionType.Bool,
Description = "Do not switch the model to 16-bit floats",
InitialValue = HardwareHelper.PreferRocm() || HardwareHelper.PreferDirectML(),
Options = ["--no-half"]
},
new()
{
Name = "Skip SD Model Download",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = ["--no-download-sd-model"]
},
new()
{
Name = "Skip Install",
Type = LaunchOptionType.Bool,
Options = ["--skip-install"]
},
LaunchOptionDefinition.Extras
];
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods =>
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };

197
StabilityMatrix.Core/Models/Packages/ComfyUI.cs

@ -16,7 +16,12 @@ using YamlDotNet.Serialization.NamingConventions;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class ComfyUI : BaseGitPackage
public class ComfyUI(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper)
: BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
public override string Name => "ComfyUI";
@ -38,14 +43,6 @@ public class ComfyUI : BaseGitPackage
public override SharedFolderMethod RecommendedSharedFolderMethod =>
SharedFolderMethod.Configuration;
public ComfyUI(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
// https://github.com/comfyanonymous/ComfyUI/blob/master/folder_paths.py#L11
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
@ -61,33 +58,36 @@ public class ComfyUI : BaseGitPackage
[SharedFolderType.GLIGEN] = new[] { "models/gligen" },
[SharedFolderType.ESRGAN] = new[] { "models/upscale_models" },
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
[SharedFolderType.IpAdapter] = new[] { "models/ipadapter" },
[SharedFolderType.T2IAdapter] = new[] { "models/controlnet" },
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
new() { [SharedOutputType.Text2Img] = new[] { "output" } };
public override List<LaunchOptionDefinition> LaunchOptions =>
new List<LaunchOptionDefinition>
[
new LaunchOptionDefinition
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "127.0.0.1",
Options = { "--listen" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "8188",
Options = { "--port" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "127.0.0.1",
Options = ["--listen"]
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "8188",
Options = ["--port"]
},
new LaunchOptionDefinition
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
@ -96,55 +96,62 @@ public class ComfyUI : BaseGitPackage
Level.Medium => "--normalvram",
_ => null
},
Options = { "--highvram", "--normalvram", "--lowvram", "--novram" }
},
new()
{
Name = "Preview Method",
Type = LaunchOptionType.Bool,
InitialValue = "--preview-method auto",
Options =
{
"--preview-method auto",
"--preview-method latent2rgb",
"--preview-method taesd"
}
},
new()
{
Name = "Enable DirectML",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferDirectML(),
Options = { "--directml" }
},
new()
{
Name = "Use CPU only",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu() && !HardwareHelper.HasAmdGpu(),
Options = { "--cpu" }
},
new()
{
Name = "Disable Xformers",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = { "--disable-xformers" }
},
new()
{
Name = "Disable upcasting of attention",
Type = LaunchOptionType.Bool,
Options = { "--dont-upcast-attention" }
},
new()
{
Name = "Auto-Launch",
Type = LaunchOptionType.Bool,
Options = { "--auto-launch" }
},
LaunchOptionDefinition.Extras
};
Options = ["--highvram", "--normalvram", "--lowvram", "--novram"]
},
new LaunchOptionDefinition
{
Name = "Preview Method",
Type = LaunchOptionType.Bool,
InitialValue = "--preview-method auto",
Options =
[
"--preview-method auto",
"--preview-method latent2rgb",
"--preview-method taesd"
]
},
new LaunchOptionDefinition
{
Name = "Enable DirectML",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferDirectML(),
Options = ["--directml"]
},
new LaunchOptionDefinition
{
Name = "Use CPU only",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu() && !HardwareHelper.HasAmdGpu(),
Options = ["--cpu"]
},
new LaunchOptionDefinition
{
Name = "Disable Xformers",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = ["--disable-xformers"]
},
new LaunchOptionDefinition
{
Name = "Disable upcasting of attention",
Type = LaunchOptionType.Bool,
Options = ["--dont-upcast-attention"]
},
new LaunchOptionDefinition
{
Name = "Auto-Launch",
Type = LaunchOptionType.Bool,
Options = ["--auto-launch"]
},
LaunchOptionDefinition.Extras
];
public override string MainBranch => "master";
@ -264,22 +271,10 @@ public class ComfyUI : BaseGitPackage
)
{
await SetupVenv(installedPackagePath).ConfigureAwait(false);
var args = $"\"{Path.Combine(installedPackagePath, command)}\" {arguments}";
void HandleConsoleOutput(ProcessOutput s)
{
onConsoleOutput?.Invoke(s);
if (s.Text.Contains("To see the GUI go to", StringComparison.OrdinalIgnoreCase))
{
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)");
var match = regex.Match(s.Text);
if (match.Success)
{
WebUrl = match.Value;
}
OnStartupComplete(WebUrl);
}
}
VenvRunner?.RunDetached(args.TrimEnd(), HandleConsoleOutput, HandleExit);
return;
void HandleExit(int i)
{
@ -287,9 +282,21 @@ public class ComfyUI : BaseGitPackage
OnExit(i);
}
var args = $"\"{Path.Combine(installedPackagePath, command)}\" {arguments}";
void HandleConsoleOutput(ProcessOutput s)
{
onConsoleOutput?.Invoke(s);
VenvRunner?.RunDetached(args.TrimEnd(), HandleConsoleOutput, HandleExit);
if (!s.Text.Contains("To see the GUI go to", StringComparison.OrdinalIgnoreCase))
return;
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)");
var match = regex.Match(s.Text);
if (match.Success)
{
WebUrl = match.Value;
}
OnStartupComplete(WebUrl);
}
}
public override Task SetupModelFolders(

15
StabilityMatrix.Core/Models/Packages/FocusControlNet.cs

@ -12,16 +12,13 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class FocusControlNet : Fooocus
public class FocusControlNet(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : Fooocus(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
public FocusControlNet(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override string Name => "Fooocus-ControlNet-SDXL";
public override string DisplayName { get; set; } = "Fooocus-ControlNet";
public override string Author => "fenneishi";

15
StabilityMatrix.Core/Models/Packages/Fooocus.cs

@ -12,16 +12,13 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class Fooocus : BaseGitPackage
public class Fooocus(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
public Fooocus(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override string Name => "Fooocus";
public override string DisplayName { get; set; } = "Fooocus";
public override string Author => "lllyasviel";

15
StabilityMatrix.Core/Models/Packages/FooocusMre.cs

@ -12,16 +12,13 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class FooocusMre : BaseGitPackage
public class FooocusMre(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
public FooocusMre(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override string Name => "Fooocus-MRE";
public override string DisplayName { get; set; } = "Fooocus-MRE";
public override string Author => "MoonRide303";

109
StabilityMatrix.Core/Models/Packages/InvokeAI.cs

@ -82,6 +82,10 @@ public class InvokeAI : BaseGitPackage
[SharedFolderType.IpAdapter] = new[]
{
Path.Combine(RelativeRootPath, "autoimport", "ip_adapter")
},
[SharedFolderType.T2IAdapter] = new[]
{
Path.Combine(RelativeRootPath, "autoimport", "t2i_adapter")
}
};
@ -98,57 +102,62 @@ public class InvokeAI : BaseGitPackage
// https://github.com/invoke-ai/InvokeAI/blob/main/docs/features/CONFIGURATION.md
public override List<LaunchOptionDefinition> LaunchOptions =>
new List<LaunchOptionDefinition>
[
new LaunchOptionDefinition
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = new List<string> { "--host" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "9090",
Options = new List<string> { "--port" }
},
new()
{
Name = "Allow Origins",
Description =
"List of host names or IP addresses that are allowed to connect to the "
+ "InvokeAI API in the format ['host1','host2',...]",
Type = LaunchOptionType.String,
DefaultValue = "[]",
Options = new List<string> { "--allow-origins" }
},
new()
{
Name = "Always use CPU",
Type = LaunchOptionType.Bool,
Options = new List<string> { "--always_use_cpu" }
},
new()
{
Name = "Precision",
Type = LaunchOptionType.Bool,
Options = new List<string>
{
"--precision auto",
"--precision float16",
"--precision float32",
}
},
new()
{
Name = "Aggressively free up GPU memory after each operation",
Type = LaunchOptionType.Bool,
Options = new List<string> { "--free_gpu_mem" }
},
LaunchOptionDefinition.Extras
};
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = ["--host"]
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "9090",
Options = ["--port"]
},
new LaunchOptionDefinition
{
Name = "Allow Origins",
Description =
"List of host names or IP addresses that are allowed to connect to the "
+ "InvokeAI API in the format ['host1','host2',...]",
Type = LaunchOptionType.String,
DefaultValue = "[]",
Options = ["--allow-origins"]
},
new LaunchOptionDefinition
{
Name = "Always use CPU",
Type = LaunchOptionType.Bool,
Options = ["--always_use_cpu"]
},
new LaunchOptionDefinition
{
Name = "Precision",
Type = LaunchOptionType.Bool,
Options =
[
"--precision auto",
"--precision float16",
"--precision float32"
]
},
new LaunchOptionDefinition
{
Name = "Aggressively free up GPU memory after each operation",
Type = LaunchOptionType.Bool,
Options = ["--free_gpu_mem"]
},
LaunchOptionDefinition.Extras
];
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.Rocm, TorchVersion.Mps };

135
StabilityMatrix.Core/Models/Packages/KohyaSs.cs

@ -13,22 +13,14 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class KohyaSs : BaseGitPackage
public class KohyaSs(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper,
IPyRunner runner)
: BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private readonly IPyRunner pyRunner;
public KohyaSs(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper,
IPyRunner pyRunner
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
this.pyRunner = pyRunner;
}
public override string Name => "kohya_ss";
public override string DisplayName { get; set; } = "kohya_ss";
public override string Author => "bmaltais";
@ -61,59 +53,66 @@ public class KohyaSs : BaseGitPackage
new[] { SharedFolderMethod.None };
public override List<LaunchOptionDefinition> LaunchOptions =>
new()
[
new LaunchOptionDefinition
{
new LaunchOptionDefinition
{
Name = "Listen Address",
Type = LaunchOptionType.String,
DefaultValue = "127.0.0.1",
Options = new List<string> { "--listen" }
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
Options = new List<string> { "--port" }
},
new LaunchOptionDefinition
{
Name = "Username",
Type = LaunchOptionType.String,
Options = new List<string> { "--username" }
},
new LaunchOptionDefinition
{
Name = "Password",
Type = LaunchOptionType.String,
Options = new List<string> { "--password" }
},
new LaunchOptionDefinition
{
Name = "Auto-Launch Browser",
Type = LaunchOptionType.Bool,
Options = new List<string> { "--inbrowser" }
},
new LaunchOptionDefinition
{
Name = "Share",
Type = LaunchOptionType.Bool,
Options = new List<string> { "--share" }
},
new LaunchOptionDefinition
{
Name = "Headless",
Type = LaunchOptionType.Bool,
Options = new List<string> { "--headless" }
},
new LaunchOptionDefinition
{
Name = "Language",
Type = LaunchOptionType.String,
Options = new List<string> { "--language" }
},
LaunchOptionDefinition.Extras
};
Name = "Listen Address",
Type = LaunchOptionType.String,
DefaultValue = "127.0.0.1",
Options = ["--listen"]
},
new LaunchOptionDefinition
{
Name = "Port",
Type = LaunchOptionType.String,
Options = ["--port"]
},
new LaunchOptionDefinition
{
Name = "Username",
Type = LaunchOptionType.String,
Options = ["--username"]
},
new LaunchOptionDefinition
{
Name = "Password",
Type = LaunchOptionType.String,
Options = ["--password"]
},
new LaunchOptionDefinition
{
Name = "Auto-Launch Browser",
Type = LaunchOptionType.Bool,
Options = ["--inbrowser"]
},
new LaunchOptionDefinition
{
Name = "Share",
Type = LaunchOptionType.Bool,
Options = ["--share"]
},
new LaunchOptionDefinition
{
Name = "Headless",
Type = LaunchOptionType.Bool,
Options = ["--headless"]
},
new LaunchOptionDefinition
{
Name = "Language",
Type = LaunchOptionType.String,
Options = ["--language"]
},
LaunchOptionDefinition.Extras
];
public override async Task InstallPackage(
string installLocation,
@ -177,7 +176,7 @@ public class KohyaSs : BaseGitPackage
await SetupVenv(installedPackagePath).ConfigureAwait(false);
// update gui files to point to venv accelerate
await pyRunner.RunInThreadWithLock(() =>
await runner.RunInThreadWithLock(() =>
{
var scope = Py.CreateScope();
scope.Exec(

20
StabilityMatrix.Core/Models/Packages/RuinedFooocus.cs

@ -1,27 +1,21 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Helper.Cache;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Models.Progress;
using StabilityMatrix.Core.Processes;
using StabilityMatrix.Core.Python;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class RuinedFooocus : Fooocus
public class RuinedFooocus(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : Fooocus(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
public RuinedFooocus(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override string Name => "RuinedFooocus";
public override string DisplayName { get; set; } = "RuinedFooocus";
public override string Author => "runew0lf";

15
StabilityMatrix.Core/Models/Packages/StableDiffusionDirectMl.cs

@ -14,7 +14,12 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class StableDiffusionDirectMl : A3WebUI
public class StableDiffusionDirectMl(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : A3WebUI(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -36,14 +41,6 @@ public class StableDiffusionDirectMl : A3WebUI
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Recommended;
public StableDiffusionDirectMl(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.DirectMl };

183
StabilityMatrix.Core/Models/Packages/StableDiffusionUx.cs

@ -14,7 +14,12 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class StableDiffusionUx : BaseGitPackage
public class StableDiffusionUx(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper)
: BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -37,14 +42,6 @@ public class StableDiffusionUx : BaseGitPackage
public override PackageDifficulty InstallerSortOrder => PackageDifficulty.Simple;
public StableDiffusionUx(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
{
@ -79,27 +76,28 @@ public class StableDiffusionUx : BaseGitPackage
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
public override List<LaunchOptionDefinition> LaunchOptions =>
[
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = ["--server-name"]
},
new()
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = new() { "--server-name" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = new() { "--port" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = ["--port"]
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
@ -108,66 +106,75 @@ public class StableDiffusionUx : BaseGitPackage
Level.Medium => "--medvram",
_ => null
},
Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" }
},
new()
{
Name = "Xformers",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = new() { "--xformers" }
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--api" }
},
new()
{
Name = "Auto Launch Web UI",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--autolaunch" }
},
new()
{
Name = "Skip Torch CUDA Check",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = new() { "--skip-torch-cuda-test" }
},
new()
{
Name = "Skip Python Version Check",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = new() { "--skip-python-version-check" }
},
new()
{
Name = "No Half",
Type = LaunchOptionType.Bool,
Description = "Do not switch the model to 16-bit floats",
InitialValue = HardwareHelper.HasAmdGpu(),
Options = new() { "--no-half" }
},
new()
{
Name = "Skip SD Model Download",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = new() { "--no-download-sd-model" }
},
new()
{
Name = "Skip Install",
Type = LaunchOptionType.Bool,
Options = new() { "--skip-install" }
},
LaunchOptionDefinition.Extras
};
Options = ["--lowvram", "--medvram", "--medvram-sdxl"]
},
new()
{
Name = "Xformers",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = ["--xformers"]
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = ["--api"]
},
new()
{
Name = "Auto Launch Web UI",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = ["--autolaunch"]
},
new()
{
Name = "Skip Torch CUDA Check",
Type = LaunchOptionType.Bool,
InitialValue = !HardwareHelper.HasNvidiaGpu(),
Options = ["--skip-torch-cuda-test"]
},
new()
{
Name = "Skip Python Version Check",
Type = LaunchOptionType.Bool,
InitialValue = true,
Options = ["--skip-python-version-check"]
},
new()
{
Name = "No Half",
Type = LaunchOptionType.Bool,
Description = "Do not switch the model to 16-bit floats",
InitialValue = HardwareHelper.PreferRocm() || HardwareHelper.PreferDirectML(),
Options = ["--no-half"]
},
new()
{
Name = "Skip SD Model Download",
Type = LaunchOptionType.Bool,
InitialValue = false,
Options = ["--no-download-sd-model"]
},
new()
{
Name = "Skip Install",
Type = LaunchOptionType.Bool,
Options = ["--skip-install"]
},
LaunchOptionDefinition.Extras
];
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods =>
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };

173
StabilityMatrix.Core/Models/Packages/VladAutomatic.cs

@ -16,7 +16,12 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class VladAutomatic : BaseGitPackage
public class VladAutomatic(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper)
: BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
@ -39,14 +44,6 @@ public class VladAutomatic : BaseGitPackage
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
public VladAutomatic(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
// https://github.com/vladmandic/automatic/blob/master/modules/shared.py#L324
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
@ -85,27 +82,28 @@ public class VladAutomatic : BaseGitPackage
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
public override List<LaunchOptionDefinition> LaunchOptions =>
[
new()
{
new()
{
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = new() { "--server-name" }
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = new() { "--port" }
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
Name = "Host",
Type = LaunchOptionType.String,
DefaultValue = "localhost",
Options = ["--server-name"]
},
new()
{
Name = "Port",
Type = LaunchOptionType.String,
DefaultValue = "7860",
Options = ["--port"]
},
new()
{
Name = "VRAM",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper
.IterGpuInfo()
.Select(gpu => gpu.MemoryLevel)
.Max() switch
@ -114,61 +112,70 @@ public class VladAutomatic : BaseGitPackage
Level.Medium => "--medvram",
_ => null
},
Options = new() { "--lowvram", "--medvram" }
},
new()
{
Name = "Auto-Launch Web UI",
Type = LaunchOptionType.Bool,
Options = new() { "--autolaunch" }
},
new()
{
Name = "Force use of Intel OneAPI XPU backend",
Type = LaunchOptionType.Bool,
Options = new() { "--use-ipex" }
},
new()
{
Name = "Use DirectML if no compatible GPU is detected",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferDirectML(),
Options = new() { "--use-directml" }
},
new()
{
Name = "Force use of Nvidia CUDA backend",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = new() { "--use-cuda" }
},
new()
{
Name = "Force use of AMD ROCm backend",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferRocm(),
Options = new() { "--use-rocm" }
},
new()
{
Name = "CUDA Device ID",
Type = LaunchOptionType.String,
Options = new() { "--device-id" }
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
Options = new() { "--api" }
},
new()
{
Name = "Debug Logging",
Type = LaunchOptionType.Bool,
Options = new() { "--debug" }
},
LaunchOptionDefinition.Extras
};
Options = ["--lowvram", "--medvram"]
},
new()
{
Name = "Auto-Launch Web UI",
Type = LaunchOptionType.Bool,
Options = ["--autolaunch"]
},
new()
{
Name = "Force use of Intel OneAPI XPU backend",
Type = LaunchOptionType.Bool,
Options = ["--use-ipex"]
},
new()
{
Name = "Use DirectML if no compatible GPU is detected",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferDirectML(),
Options = ["--use-directml"]
},
new()
{
Name = "Force use of Nvidia CUDA backend",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.HasNvidiaGpu(),
Options = ["--use-cuda"]
},
new()
{
Name = "Force use of AMD ROCm backend",
Type = LaunchOptionType.Bool,
InitialValue = HardwareHelper.PreferRocm(),
Options = ["--use-rocm"]
},
new()
{
Name = "CUDA Device ID",
Type = LaunchOptionType.String,
Options = ["--device-id"]
},
new()
{
Name = "API",
Type = LaunchOptionType.Bool,
Options = ["--api"]
},
new()
{
Name = "Debug Logging",
Type = LaunchOptionType.Bool,
Options = ["--debug"]
},
LaunchOptionDefinition.Extras
];
public override string ExtraLaunchArguments => "";

15
StabilityMatrix.Core/Models/Packages/VoltaML.cs

@ -10,7 +10,12 @@ using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Core.Models.Packages;
[Singleton(typeof(BasePackage))]
public class VoltaML : BaseGitPackage
public class VoltaML(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
) : BaseGitPackage(githubApi, settingsManager, downloadService, prerequisiteHelper)
{
public override string Name => "voltaML-fast-stable-diffusion";
public override string DisplayName { get; set; } = "VoltaML";
@ -32,14 +37,6 @@ public class VoltaML : BaseGitPackage
// so we'll just limit to commit mode to be more consistent
public override bool ShouldIgnoreReleases => true;
public VoltaML(
IGithubApiCache githubApi,
ISettingsManager settingsManager,
IDownloadService downloadService,
IPrerequisiteHelper prerequisiteHelper
)
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
// https://github.com/VoltaML/voltaML-fast-stable-diffusion/blob/main/main.py#L86
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()

4
StabilityMatrix.Core/Models/SharedFolderType.cs

@ -8,6 +8,7 @@ namespace StabilityMatrix.Core.Models;
[Flags]
public enum SharedFolderType
{
[Description("Base Models")]
StableDiffusion = 1 << 0,
Lora = 1 << 1,
LyCORIS = 1 << 2,
@ -32,5 +33,6 @@ public enum SharedFolderType
ScuNET = 1 << 19,
GLIGEN = 1 << 20,
AfterDetailer = 1 << 21,
IpAdapter = 1 << 22
IpAdapter = 1 << 22,
T2IAdapter = 1 << 23,
}

Loading…
Cancel
Save