Browse Source

Merge pull request #317 from ionite34/consolidate

Added Consolidate button to move all images to shared output directory
pull/240/head
JT 1 year ago committed by GitHub
parent
commit
61e108179e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 27
      StabilityMatrix.Avalonia/Languages/Resources.Designer.cs
  2. 9
      StabilityMatrix.Avalonia/Languages/Resources.resx
  3. 21
      StabilityMatrix.Avalonia/ViewModels/CheckpointBrowser/CheckpointBrowserCardViewModel.cs
  4. 146
      StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs
  5. 21
      StabilityMatrix.Avalonia/Views/OutputsPage.axaml
  6. 40
      StabilityMatrix.Core/Models/Api/CivitModel.cs
  7. 27
      StabilityMatrix.Core/Models/Packages/InvokeAI.cs
  8. 3
      StabilityMatrix.Core/Models/SharedOutputType.cs
  9. 1
      StabilityMatrix.Core/Services/ISettingsManager.cs
  10. 4
      StabilityMatrix.Core/Services/ImageIndexService.cs
  11. 1
      StabilityMatrix.Core/Services/SettingsManager.cs

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

@ -140,6 +140,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Consolidate.
/// </summary>
public static string Action_Consolidate {
get {
return ResourceManager.GetString("Action_Consolidate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Continue.
/// </summary>
@ -554,6 +563,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure?.
/// </summary>
public static string Label_AreYouSure {
get {
return ResourceManager.GetString("Label_AreYouSure", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Automatically scroll to end of console output.
/// </summary>
@ -707,6 +725,15 @@ namespace StabilityMatrix.Avalonia.Languages {
}
}
/// <summary>
/// Looks up a localized string similar to This will move all generated images from the selected packages to the Consolidated directory of the shared outputs folder. This action cannot be undone..
/// </summary>
public static string Label_ConsolidateExplanation {
get {
return ResourceManager.GetString("Label_ConsolidateExplanation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Current directory:.
/// </summary>

9
StabilityMatrix.Avalonia/Languages/Resources.resx

@ -726,4 +726,13 @@
<data name="Label_OneImageSelected" xml:space="preserve">
<value>1 image selected</value>
</data>
<data name="Action_Consolidate" xml:space="preserve">
<value>Consolidate</value>
</data>
<data name="Label_AreYouSure" xml:space="preserve">
<value>Are you sure?</value>
</data>
<data name="Label_ConsolidateExplanation" xml:space="preserve">
<value>This will move all generated images from the selected packages to the Consolidated directory of the shared outputs folder. This action cannot be undone.</value>
</data>
</root>

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

@ -257,16 +257,17 @@ public partial class CheckpointBrowserCardViewModel : Base.ProgressViewModel
private static string PruneDescription(CivitModel model)
{
var prunedDescription = model.Description
.Replace("<br/>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("<br />", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</p>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h1>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h2>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h3>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h4>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h5>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h6>", $"{Environment.NewLine}{Environment.NewLine}");
var prunedDescription =
model.Description
?.Replace("<br/>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("<br />", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</p>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h1>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h2>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h3>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h4>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h5>", $"{Environment.NewLine}{Environment.NewLine}")
.Replace("</h6>", $"{Environment.NewLine}{Environment.NewLine}") ?? string.Empty;
prunedDescription = HtmlRegex().Replace(prunedDescription, string.Empty);
return prunedDescription;
}

146
StabilityMatrix.Avalonia/ViewModels/OutputsPageViewModel.cs

@ -8,7 +8,9 @@ using System.Reactive.Linq;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using AsyncImageLoader;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using DynamicData;
@ -81,6 +83,9 @@ public partial class OutputsPageViewModel : PageViewModelBase
[ObservableProperty]
private Size imageSize = new(300, 300);
[ObservableProperty]
private bool isConsolidating;
public bool CanShowOutputTypes =>
SelectedCategory?.Name?.Equals("Shared Output Folder") ?? false;
@ -115,8 +120,8 @@ public partial class OutputsPageViewModel : PageViewModelBase
.Connect()
.DeferUntilLoaded()
.Filter(searchPredicate)
.SortBy(file => file.CreatedAt, SortDirection.Descending)
.Transform(file => new OutputImageViewModel(file))
.SortBy(vm => vm.ImageFile.CreatedAt, SortDirection.Descending)
.Bind(Outputs)
.WhenPropertyChanged(p => p.IsSelected)
.Subscribe(_ =>
@ -141,6 +146,7 @@ public partial class OutputsPageViewModel : PageViewModelBase
return;
Directory.CreateDirectory(settingsManager.ImagesDirectory);
var packageCategories = settingsManager.Settings.InstalledPackages
.Where(x => !x.UseSharedOutputFolder)
.Select(p =>
@ -166,6 +172,15 @@ public partial class OutputsPageViewModel : PageViewModelBase
}
);
packageCategories.Insert(
1,
new PackageOutputCategory
{
Path = settingsManager.ImagesInferenceDirectory,
Name = "Inference"
}
);
Categories = new ObservableCollection<PackageOutputCategory>(packageCategories);
SelectedCategory = Categories.First();
SelectedOutputType = SharedOutputType.All;
@ -206,7 +221,7 @@ public partial class OutputsPageViewModel : PageViewModelBase
GetOutputs(path);
}
public async Task OnImageClick(OutputImageViewModel item)
public Task OnImageClick(OutputImageViewModel item)
{
// Select image if we're in "select mode"
if (NumItemsSelected > 0)
@ -215,8 +230,10 @@ public partial class OutputsPageViewModel : PageViewModelBase
}
else
{
await ShowImageDialog(item);
return ShowImageDialog(item);
}
return Task.CompletedTask;
}
public async Task ShowImageDialog(OutputImageViewModel item)
@ -265,14 +282,13 @@ public partial class OutputsPageViewModel : PageViewModelBase
await vm.GetDialog().ShowAsync();
}
public async Task CopyImage(string imagePath)
public Task CopyImage(string imagePath)
{
var clipboard = App.Clipboard;
await clipboard.SetFileDataObjectAsync(imagePath);
return clipboard.SetFileDataObjectAsync(imagePath);
}
public async Task OpenImage(string imagePath) => await ProcessRunner.OpenFileBrowser(imagePath);
public Task OpenImage(string imagePath) => ProcessRunner.OpenFileBrowser(imagePath);
public async Task DeleteImage(OutputImageViewModel? item)
{
@ -378,12 +394,114 @@ public partial class OutputsPageViewModel : PageViewModelBase
ClearSelection();
}
public async Task ConsolidateImages()
{
var stackPanel = new StackPanel();
stackPanel.Children.Add(
new TextBlock
{
Text = Resources.Label_ConsolidateExplanation,
TextWrapping = TextWrapping.Wrap,
Margin = new Thickness(0, 8, 0, 16)
}
);
foreach (var category in Categories)
{
if (category.Name == "Shared Output Folder")
{
continue;
}
stackPanel.Children.Add(
new CheckBox
{
Content = $"{category.Name} ({category.Path})",
IsChecked = true,
Margin = new Thickness(0, 8, 0, 0),
Tag = category.Path
}
);
}
var confirmationDialog = new BetterContentDialog
{
Title = Resources.Label_AreYouSure,
Content = stackPanel,
PrimaryButtonText = Resources.Action_Yes,
SecondaryButtonText = Resources.Action_Cancel,
DefaultButton = ContentDialogButton.Primary,
IsSecondaryButtonEnabled = true,
};
var dialogResult = await confirmationDialog.ShowAsync();
if (dialogResult != ContentDialogResult.Primary)
return;
IsConsolidating = true;
Directory.CreateDirectory(settingsManager.ConsolidatedImagesDirectory);
foreach (
var category in stackPanel.Children.OfType<CheckBox>().Where(c => c.IsChecked == true)
)
{
if (
string.IsNullOrWhiteSpace(category.Tag?.ToString())
|| !Directory.Exists(category.Tag?.ToString())
)
continue;
var directory = category.Tag.ToString();
foreach (
var path in Directory.EnumerateFiles(
directory,
"*.png",
SearchOption.AllDirectories
)
)
{
try
{
var file = new FilePath(path);
var newPath = settingsManager.ConsolidatedImagesDirectory + file.Name;
if (file.FullPath == newPath)
continue;
// ignore inference if not in inference directory
if (
file.FullPath.Contains(settingsManager.ImagesInferenceDirectory)
&& directory != settingsManager.ImagesInferenceDirectory
)
{
continue;
}
await file.MoveToAsync(newPath);
}
catch (Exception e)
{
logger.LogError(e, "Error when consolidating: ");
}
}
}
OnLoaded();
IsConsolidating = false;
}
private void GetOutputs(string directory)
{
if (!settingsManager.IsLibraryDirSet)
return;
if (!Directory.Exists(directory) && SelectedOutputType != SharedOutputType.All)
if (
!Directory.Exists(directory)
&& (
SelectedCategory.Path != settingsManager.ImagesDirectory
|| SelectedOutputType != SharedOutputType.All
)
)
{
Directory.CreateDirectory(directory);
return;
@ -391,8 +509,16 @@ public partial class OutputsPageViewModel : PageViewModelBase
var files = Directory
.EnumerateFiles(directory, "*.png", SearchOption.AllDirectories)
.Select(file => LocalImageFile.FromPath(file));
.Select(file => LocalImageFile.FromPath(file))
.ToList();
OutputsCache.EditDiff(files);
if (files.Count == 0)
{
OutputsCache.Clear();
}
else
{
OutputsCache.EditDiff(files);
}
}
}

21
StabilityMatrix.Avalonia/Views/OutputsPage.axaml

@ -79,6 +79,27 @@
Margin="4, 0"
VerticalContentAlignment="Center"
MinWidth="150"/>
<Button Grid.Row="1"
Grid.Column="3"
VerticalAlignment="Stretch"
Margin="4, 0"
IsEnabled="{Binding !IsConsolidating}"
Command="{Binding ConsolidateImages}">
<StackPanel Orientation="Horizontal">
<TextBlock
Text="{x:Static lang:Resources.Action_Consolidate}"
VerticalAlignment="Center"/>
<controls:ProgressRing
MinHeight="16"
Margin="8, 0"
IsIndeterminate="True"
VerticalAlignment="Center"
BorderThickness="4"
IsVisible="{Binding IsConsolidating}"
MinWidth="16" />
</StackPanel>
</Button>
<TextBlock Grid.Row="1"
Grid.Column="4"

40
StabilityMatrix.Core/Models/Api/CivitModel.cs

@ -7,28 +7,28 @@ public class CivitModel
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("description")]
public string Description { get; set; }
public string? Description { get; set; }
[JsonPropertyName("type")]
public CivitModelType Type { get; set; }
[JsonPropertyName("nsfw")]
public bool Nsfw { get; set; }
[JsonPropertyName("tags")]
public string[] Tags { get; set; }
[JsonPropertyName("mode")]
public CivitMode? Mode { get; set; }
[JsonPropertyName("creator")]
public CivitCreator Creator { get; set; }
[JsonPropertyName("stats")]
public CivitModelStats Stats { get; set; }
@ -41,14 +41,16 @@ public class CivitModel
{
get
{
if (fullFilesSize != null) return fullFilesSize;
if (fullFilesSize != null)
return fullFilesSize;
var kbs = 0.0;
var latestVersion = ModelVersions?.FirstOrDefault();
if (latestVersion?.Files != null && latestVersion.Files.Any())
{
var latestModelFile =
latestVersion.Files.FirstOrDefault(x => x.Type == CivitFileType.Model);
var latestModelFile = latestVersion.Files.FirstOrDefault(
x => x.Type == CivitFileType.Model
);
kbs = latestModelFile?.SizeKb ?? 0;
}
fullFilesSize = new FileSizeType(kbs);
@ -56,11 +58,11 @@ public class CivitModel
}
}
public string LatestModelVersionName => ModelVersions != null && ModelVersions.Any()
? ModelVersions[0].Name
: string.Empty;
public string LatestModelVersionName =>
ModelVersions != null && ModelVersions.Any() ? ModelVersions[0].Name : string.Empty;
public string? BaseModelType => ModelVersions != null && ModelVersions.Any()
? ModelVersions[0].BaseModel?.Replace("SD", "").Trim()
: string.Empty;
public string? BaseModelType =>
ModelVersions != null && ModelVersions.Any()
? ModelVersions[0].BaseModel?.Replace("SD", "").Trim()
: string.Empty;
}

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

@ -63,19 +63,34 @@ public class InvokeAI : BaseGitPackage
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
new()
{
[SharedFolderType.StableDiffusion] = new[] { RelativeRootPath + "/autoimport/main" },
[SharedFolderType.Lora] = new[] { RelativeRootPath + "/autoimport/lora" },
[SharedFolderType.StableDiffusion] = new[]
{
Path.Combine(RelativeRootPath, "autoimport", "main")
},
[SharedFolderType.Lora] = new[]
{
Path.Combine(RelativeRootPath, "autoimport", "lora")
},
[SharedFolderType.TextualInversion] = new[]
{
RelativeRootPath + "/autoimport/embedding"
Path.Combine(RelativeRootPath, "autoimport", "embedding")
},
[SharedFolderType.ControlNet] = new[] { RelativeRootPath + "/autoimport/controlnet" },
[SharedFolderType.ControlNet] = new[]
{
Path.Combine(RelativeRootPath, "autoimport", "controlnet")
}
};
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
new() { [SharedOutputType.Text2Img] = new[] { "invokeai-root/outputs/images" } };
new()
{
[SharedOutputType.Text2Img] = new[]
{
Path.Combine("invokeai-root", "outputs", "images")
}
};
public override string OutputFolderName => "invokeai-root/outputs/images";
public override string OutputFolderName => Path.Combine("invokeai-root", "outputs", "images");
// https://github.com/invoke-ai/InvokeAI/blob/main/docs/features/CONFIGURATION.md
public override List<LaunchOptionDefinition> LaunchOptions =>

3
StabilityMatrix.Core/Models/SharedOutputType.cs

@ -8,5 +8,6 @@ public enum SharedOutputType
Extras,
Text2ImgGrids,
Img2ImgGrids,
Saved
Saved,
Consolidated
}

1
StabilityMatrix.Core/Services/ISettingsManager.cs

@ -21,6 +21,7 @@ public interface ISettingsManager
List<string> PackageInstallsInProgress { get; set; }
Settings Settings { get; }
DirectoryPath ConsolidatedImagesDirectory { get; }
/// <summary>
/// Event fired when the library directory is changed

4
StabilityMatrix.Core/Services/ImageIndexService.cs

@ -56,13 +56,13 @@ public class ImageIndexService : IImageIndexService
// Start
var stopwatch = Stopwatch.StartNew();
logger.LogInformation("Refreshing images index at {ImagesDir}...", imagesDir);
logger.LogInformation("Refreshing images index at {SearchDir}...", searchDir);
var toAdd = new ConcurrentBag<LocalImageFile>();
await Task.Run(() =>
{
var files = imagesDir
var files = searchDir
.EnumerateFiles("*.*", SearchOption.AllDirectories)
.Where(
file => LocalImageFile.SupportedImageExtensions.Contains(file.Extension)

1
StabilityMatrix.Core/Services/SettingsManager.cs

@ -71,6 +71,7 @@ public class SettingsManager : ISettingsManager
public DirectoryPath ImagesDirectory => new(LibraryDir, "Images");
public DirectoryPath ImagesInferenceDirectory => ImagesDirectory.JoinDir("Inference");
public DirectoryPath ConsolidatedImagesDirectory => ImagesDirectory.JoinDir("Consolidated");
public Settings Settings { get; private set; } = new();

Loading…
Cancel
Save