diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c5e3c2d2..69c61c948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to Stability Matrix will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning 2.0](https://semver.org/spec/v2.0.0.html). +## v2.8.3 +### Fixed +- Fixed user tokens read error causing failed downloads +- Failed downloads will now log error messages +- Fixed [#458](https://github.com/LykosAI/StabilityMatrix/issues/458) - Save Intermediate Image not working +- Fixed [#453](https://github.com/LykosAI/StabilityMatrix/issues/453) - Update Fooocus `--output-directory` argument to `--output-path` + ## v2.8.2 ### Added - Added missing GFPGAN link to Automatic1111 packages diff --git a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs index 9cbf2e182..7ab57d5a2 100644 --- a/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs +++ b/StabilityMatrix.Avalonia/ViewModels/Base/InferenceGenerationViewModelBase.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; -using System.Management; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; @@ -15,7 +15,6 @@ using Avalonia.Threading; using CommunityToolkit.Mvvm.Input; using ExifLibrary; -using MetadataExtractor.Formats.Exif; using NLog; using Refit; using SkiaSharp; @@ -27,7 +26,6 @@ using StabilityMatrix.Avalonia.ViewModels.Dialogs; using StabilityMatrix.Avalonia.ViewModels.Inference; using StabilityMatrix.Avalonia.ViewModels.Inference.Modules; -using StabilityMatrix.Core.Animation; using StabilityMatrix.Core.Exceptions; using StabilityMatrix.Core.Extensions; using StabilityMatrix.Core.Helper; @@ -297,14 +295,18 @@ protected async Task RunGeneration(ImageGenerationEventArgs args, CancellationTo Task.Run( async () => { - var delayTime = 250 - (int)timer.ElapsedMilliseconds; - if (delayTime > 0) + try { - await Task.Delay(delayTime, cancellationToken); + var delayTime = 250 - (int)timer.ElapsedMilliseconds; + if (delayTime > 0) + { + await Task.Delay(delayTime, cancellationToken); + } + + // ReSharper disable once AccessToDisposedClosure + AttachRunningNodeChangedHandler(promptTask); } - - // ReSharper disable once AccessToDisposedClosure - AttachRunningNodeChangedHandler(promptTask); + catch (TaskCanceledException) { } }, cancellationToken ) @@ -328,10 +330,7 @@ await DialogHelper // Get output images var imageOutputs = await client.GetImagesForExecutedPromptAsync(promptTask.Id, cancellationToken); - if ( - !imageOutputs.TryGetValue(args.OutputNodeNames[0], out var images) - || images is not { Count: > 0 } - ) + if (imageOutputs.Values.All(images => images is null or { Count: 0 })) { // No images match notificationService.Show( @@ -350,7 +349,7 @@ await DialogHelper ImageGalleryCardViewModel.ImageSources.Clear(); } - var outputImages = await ProcessOutputImages(images, args); + var outputImages = await ProcessAllOutputImages(imageOutputs, args); var notificationImage = outputImages.FirstOrDefault()?.LocalFile; @@ -380,12 +379,34 @@ await notificationService.ShowAsync( } } + private async Task> ProcessAllOutputImages( + IReadOnlyDictionary?> images, + ImageGenerationEventArgs args + ) + { + var results = new List(); + + foreach (var (nodeName, imageList) in images) + { + if (imageList is null) + { + Logger.Warn("No images for node {NodeName}", nodeName); + continue; + } + + results.AddRange(await ProcessOutputImages(imageList, args, nodeName.Replace('_', ' '))); + } + + return results; + } + /// /// Handles image output metadata for generation runs /// private async Task> ProcessOutputImages( IReadOnlyCollection images, - ImageGenerationEventArgs args + ImageGenerationEventArgs args, + string? imageLabel = null ) { var client = args.Client; @@ -441,7 +462,7 @@ ImageGenerationEventArgs args images.Count ); - outputImages.Add(new ImageSource(filePath)); + outputImages.Add(new ImageSource(filePath) { Label = imageLabel }); EventManager.Instance.OnImageFileAdded(filePath); } else if (comfyImage.FileName.EndsWith(".webp")) @@ -470,7 +491,7 @@ ImageGenerationEventArgs args fileExtension: Path.GetExtension(comfyImage.FileName).Replace(".", "") ); - outputImages.Add(new ImageSource(filePath)); + outputImages.Add(new ImageSource(filePath) { Label = imageLabel }); EventManager.Instance.OnImageFileAdded(filePath); } else @@ -484,7 +505,7 @@ ImageGenerationEventArgs args fileExtension: Path.GetExtension(comfyImage.FileName).Replace(".", "") ); - outputImages.Add(new ImageSource(filePath)); + outputImages.Add(new ImageSource(filePath) { Label = imageLabel }); EventManager.Instance.OnImageFileAdded(filePath); } } @@ -554,7 +575,12 @@ private async Task GenerateImage( } catch (OperationCanceledException) { - Logger.Debug($"Image Generation Canceled"); + Logger.Debug("Image Generation Canceled"); + } + catch (ValidationException e) + { + Logger.Debug("Image Generation Validation Error: {Message}", e.Message); + notificationService.Show("Validation Error", e.Message, NotificationType.Error); } } diff --git a/StabilityMatrix.Core/Models/Packages/Fooocus.cs b/StabilityMatrix.Core/Models/Packages/Fooocus.cs index 7a4540d56..2faaab4f7 100644 --- a/StabilityMatrix.Core/Models/Packages/Fooocus.cs +++ b/StabilityMatrix.Core/Models/Packages/Fooocus.cs @@ -73,7 +73,7 @@ IPrerequisiteHelper prerequisiteHelper Name = "Output Directory", Type = LaunchOptionType.String, Description = "Override the output directory", - Options = { "--output-directory" } + Options = { "--output-path" } }, new LaunchOptionDefinition { diff --git a/StabilityMatrix.Core/Models/TrackedDownload.cs b/StabilityMatrix.Core/Models/TrackedDownload.cs index fcf5f2541..55175d9bc 100644 --- a/StabilityMatrix.Core/Models/TrackedDownload.cs +++ b/StabilityMatrix.Core/Models/TrackedDownload.cs @@ -317,6 +317,8 @@ private void OnDownloadTaskCompleted(Task task) return; } + Logger.Warn(Exception, "Download {Download} failed", FileName); + OnProgressStateChanging(ProgressState.Failed); ProgressState = ProgressState.Failed; } diff --git a/StabilityMatrix.Core/Services/DownloadService.cs b/StabilityMatrix.Core/Services/DownloadService.cs index 60adf231f..1aeeb9d76 100644 --- a/StabilityMatrix.Core/Services/DownloadService.cs +++ b/StabilityMatrix.Core/Services/DownloadService.cs @@ -326,7 +326,7 @@ private async Task AddConditionalHeaders(HttpClient client, Uri url) if (url.Host.Equals("civitai.com", StringComparison.OrdinalIgnoreCase)) { // Add auth if we have it - if (await secretsManager.LoadAsync().ConfigureAwait(false) is { CivitApi: { } civitApi }) + if (await secretsManager.SafeLoadAsync().ConfigureAwait(false) is { CivitApi: { } civitApi }) { logger.LogTrace( "Adding Civit auth header {Signature} for download {Url}", diff --git a/StabilityMatrix.Core/Services/SecretsManager.cs b/StabilityMatrix.Core/Services/SecretsManager.cs index 2c3c534eb..1ba0077ce 100644 --- a/StabilityMatrix.Core/Services/SecretsManager.cs +++ b/StabilityMatrix.Core/Services/SecretsManager.cs @@ -45,11 +45,7 @@ public async Task SafeLoadAsync() } catch (Exception e) { - logger.LogWarning( - e, - "Failed to load secrets ({ExcType}), saving new instance", - e.GetType().Name - ); + logger.LogError(e, "Failed to load secrets ({ExcType}), saving new instance", e.GetType().Name); var secrets = new Secrets(); await SaveAsync(secrets).ConfigureAwait(false);