JT
12 months ago
29 changed files with 1428 additions and 535 deletions
@ -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" |
||||
} |
||||
] |
@ -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, |
||||
} |
@ -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; } |
||||
} |
@ -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; |
||||
} |
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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); |
||||
}); |
||||
} |
||||
} |
@ -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> |
@ -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(); |
||||
} |
||||
} |
Loading…
Reference in new issue