Browse Source

Added the ability to choose where CivitAI model downloads are saved

pull/495/head
JT 9 months ago
parent
commit
6c7c6ead56
  1. 1
      CHANGELOG.md
  2. 37
      StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
  3. 99
      StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs
  4. 53
      StabilityMatrix.Avalonia/Views/Dialogs/SelectModelVersionDialog.axaml

1
CHANGELOG.md

@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2
- Added new package: [StableSwarmUI](https://github.com/Stability-AI/StableSwarmUI) by Stability AI
- Added new package: [Stable Diffusion WebUI Forge](https://github.com/lllyasviel/stable-diffusion-webui-forge) by lllyasviel
- Added extension management for SD.Next and Stable Diffusion WebUI-UX
- Added the ability to choose where CivitAI model downloads are saved
## v2.8.3
### Fixed

37
StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs

@ -190,13 +190,6 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
IsFavorite = settingsManager.Settings.FavoriteModels.Contains(CivitModel.Id);
}
[RelayCommand]
private async Task Import(CivitModel model)
{
await DoImport(model);
CheckIfInstalled();
}
[RelayCommand]
private async Task ShowVersionDialog(CivitModel model)
{
@ -220,7 +213,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
MaxDialogWidth = 750,
MaxDialogHeight = 850,
MaxDialogHeight = 950,
};
var prunedDescription = PruneDescription(model);
@ -229,6 +222,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
viewModel.Dialog = dialog;
viewModel.Title = model.Name;
viewModel.Description = prunedDescription;
viewModel.CivitModel = model;
viewModel.Versions = versions
.Select(
version =>
@ -252,8 +246,21 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
var selectedVersion = viewModel?.SelectedVersionViewModel?.ModelVersion;
var selectedFile = viewModel?.SelectedFile?.CivitFile;
DirectoryPath downloadPath;
if (viewModel?.IsCustomSelected is true)
{
downloadPath = viewModel.CustomInstallLocation;
}
else
{
var subFolder =
viewModel?.SelectedInstallLocation
?? Path.Combine("Models", model.Type.ConvertTo<SharedFolderType>().GetStringValue());
downloadPath = Path.Combine(settingsManager.LibraryDir, subFolder);
}
await Task.Delay(100);
await DoImport(model, selectedVersion, selectedFile);
await DoImport(model, downloadPath, selectedVersion, selectedFile);
}
private static string PruneDescription(CivitModel model)
@ -322,6 +329,7 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
private async Task DoImport(
CivitModel model,
DirectoryPath downloadFolder,
CivitModelVersion? selectedVersion = null,
CivitFile? selectedFile = null
)
@ -362,18 +370,13 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
return;
}
var rootModelsDirectory = new DirectoryPath(settingsManager.ModelsDirectory);
var downloadDirectory = rootModelsDirectory.JoinDir(
model.Type.ConvertTo<SharedFolderType>().GetStringValue()
);
// Folders might be missing if user didn't install any packages yet
downloadDirectory.Create();
downloadFolder.Create();
var downloadPath = downloadDirectory.JoinFile(modelFile.Name);
var downloadPath = downloadFolder.JoinFile(modelFile.Name);
// Download model info and preview first
var cmInfoPath = await SaveCmInfo(model, modelVersion, modelFile, downloadDirectory);
var cmInfoPath = await SaveCmInfo(model, modelVersion, modelFile, downloadFolder);
var previewImagePath = await SavePreviewImage(modelVersion, downloadPath);
// Create tracked download

99
StabilityMatrix.Avalonia/ViewModels/Dialogs/SelectModelVersionViewModel.cs

@ -6,6 +6,7 @@ using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Notifications;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls;
@ -15,7 +16,10 @@ using StabilityMatrix.Avalonia.Models;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Extensions;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Models;
using StabilityMatrix.Core.Models.Api;
using StabilityMatrix.Core.Models.FileInterfaces;
using StabilityMatrix.Core.Services;
@ -23,17 +27,20 @@ namespace StabilityMatrix.Avalonia.ViewModels.Dialogs;
[ManagedService]
[Transient]
public partial class SelectModelVersionViewModel : ContentDialogViewModelBase
public partial class SelectModelVersionViewModel(
ISettingsManager settingsManager,
IDownloadService downloadService,
IModelIndexService modelIndexService,
INotificationService notificationService
) : ContentDialogViewModelBase
{
private readonly ISettingsManager settingsManager;
private readonly IDownloadService downloadService;
private readonly IModelIndexService modelIndexService;
private readonly INotificationService notificationService;
private readonly IDownloadService downloadService = downloadService;
public required ContentDialog Dialog { get; set; }
public required IReadOnlyList<ModelVersionViewModel> Versions { get; set; }
public required string Description { get; set; }
public required string Title { get; set; }
public required CivitModel CivitModel { get; set; }
[ObservableProperty]
private Bitmap? previewImage;
@ -63,25 +70,25 @@ public partial class SelectModelVersionViewModel : ContentDialogViewModelBase
[ObservableProperty]
private string importTooltip = string.Empty;
public int DisplayedPageNumber => SelectedImageIndex + 1;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsCustomSelected))]
private string selectedInstallLocation = string.Empty;
public SelectModelVersionViewModel(
ISettingsManager settingsManager,
IDownloadService downloadService,
IModelIndexService modelIndexService,
INotificationService notificationService
)
{
this.settingsManager = settingsManager;
this.downloadService = downloadService;
this.modelIndexService = modelIndexService;
this.notificationService = notificationService;
}
[ObservableProperty]
private ObservableCollection<string> availableInstallLocations = [];
[ObservableProperty]
private string customInstallLocation = string.Empty;
public bool IsCustomSelected => SelectedInstallLocation == "Custom...";
public int DisplayedPageNumber => SelectedImageIndex + 1;
public override void OnLoaded()
{
SelectedVersionViewModel = Versions[0];
CanGoToNextImage = true;
LoadInstallLocations();
}
partial void OnSelectedVersionViewModelChanged(ModelVersionViewModel? value)
@ -235,6 +242,18 @@ public partial class SelectModelVersionViewModel : ContentDialogViewModelBase
}
}
partial void OnSelectedInstallLocationChanged(string value)
{
if (value.Equals("Custom...", StringComparison.OrdinalIgnoreCase))
{
Dispatcher.UIThread.InvokeAsync(SelectCustomFolder);
}
else
{
CustomInstallLocation = string.Empty;
}
}
public void PreviousImage()
{
if (SelectedImageIndex > 0)
@ -250,4 +269,48 @@ public partial class SelectModelVersionViewModel : ContentDialogViewModelBase
CanGoToPreviousImage = SelectedImageIndex > 0;
CanGoToNextImage = SelectedImageIndex < ImageUrls.Count - 1;
}
public async Task SelectCustomFolder()
{
var files = await App.StorageProvider.OpenFolderPickerAsync(
new FolderPickerOpenOptions
{
Title = "Select Download Folder",
AllowMultiple = false,
SuggestedStartLocation = await App.StorageProvider.TryGetFolderFromPathAsync(
Path.Combine(
settingsManager.ModelsDirectory,
CivitModel.Type.ConvertTo<SharedFolderType>().GetStringValue()
)
)
}
);
if (files.FirstOrDefault()?.TryGetLocalPath() is { } path)
{
CustomInstallLocation = path;
}
}
private void LoadInstallLocations()
{
var rootModelsDirectory = new DirectoryPath(settingsManager.ModelsDirectory);
var downloadDirectory = rootModelsDirectory.JoinDir(
CivitModel.Type.ConvertTo<SharedFolderType>().GetStringValue()
);
var installLocations = new ObservableCollection<string>
{
downloadDirectory.ToString().Replace(rootModelsDirectory, "Models")
};
foreach (var directory in downloadDirectory.EnumerateDirectories())
{
installLocations.Add(directory.ToString().Replace(rootModelsDirectory, "Models"));
}
installLocations.Add("Custom...");
AvailableInstallLocations = installLocations;
SelectedInstallLocation = installLocations.FirstOrDefault();
}
}

53
StabilityMatrix.Avalonia/Views/Dialogs/SelectModelVersionDialog.axaml

@ -21,7 +21,7 @@
Margin="8"
MinHeight="450"
MinWidth="700"
RowDefinitions="Auto, *, Auto"
RowDefinitions="Auto, Auto, *, Auto"
ColumnDefinitions="*,Auto,*">
<Grid.Resources>
@ -86,8 +86,12 @@
</StackPanel>
</Grid>
<Border Grid.Row="0" Grid.Column="1"
MaxHeight="360"
Background="#33000000"
CornerRadius="4"/>
<ListBox Grid.Row="0" Grid.Column="1"
Margin="8"
Margin="0, 8, 0, 8"
MaxHeight="360"
VerticalAlignment="Top"
ItemsSource="{Binding Versions}"
@ -120,15 +124,22 @@
<ListBox.Template>
<ControlTemplate>
<ScrollViewer>
<ItemsPresenter />
<ItemsPresenter Margin="8" />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
</ListBox>
<Border Grid.Row="0" Grid.Column="2"
MaxHeight="360"
Background="#33000000"
Margin="8,0"
CornerRadius="4"/>
<ListBox Grid.Row="0" Grid.Column="2"
MaxHeight="360"
Margin="8,8,8,0"
VerticalAlignment="Top"
Background="#AA000000"
ItemsSource="{Binding SelectedVersionViewModel.CivitFileViewModels}"
SelectedItem="{Binding SelectedFile}">
<ListBox.Template>
@ -179,20 +190,48 @@
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="0,8"
Orientation="Vertical"
HorizontalAlignment="Center"
IsVisible="{Binding !SelectedFile.IsInstalled}">
<TextBlock Text="Choose Download Location:"
TextAlignment="Center"
HorizontalAlignment="Center"
Margin="0,4"/>
<ComboBox ItemsSource="{Binding AvailableInstallLocations}"
SelectedItem="{Binding SelectedInstallLocation}"
HorizontalAlignment="Center"
MinWidth="300"/>
<StackPanel Orientation="Horizontal"
Margin="0,4"
HorizontalAlignment="Center"
IsVisible="{Binding IsCustomSelected}">
<TextBox Text="{Binding CustomInstallLocation}"
MinWidth="300"/>
<Button Margin="4,0"
Command="{Binding SelectCustomFolder}">
<controls1:SymbolIcon Symbol="OpenFolder" />
</Button>
</StackPanel>
</StackPanel>
<Expander Grid.Column="0"
Grid.ColumnSpan="3"
Grid.Row="1"
Grid.Row="2"
Header="{x:Static lang:Resources.Label_ModelDescription}"
ExpandDirection="Down"
Margin="8, 16">
Margin="8, 8">
<ScrollViewer MaxHeight="200">
<TextBlock Text="{Binding Description}"
TextWrapping="Wrap"
Margin="4"/>
</ScrollViewer>
</Expander>
<StackPanel Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="2"
<StackPanel Grid.Column="0" Grid.ColumnSpan="3" Grid.Row="3"
Orientation="Horizontal"
Margin="0,8,0,0"
HorizontalAlignment="Center">

Loading…
Cancel
Save