From 22c5d10e8a6498c90ae69aa18927f8a8cdafd732 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 15 Dec 2025 13:26:55 +0100 Subject: [PATCH] Filtered out unsupported audio files in file handling (#592) --- .../Components/AttachDocuments.razor.cs | 29 +--------- .../Components/ReadFileContent.razor.cs | 22 +------- .../Tools/Rust/FileTypeFilter.cs | 4 +- .../Validation/FileExtensionValidation.cs | 55 +++++++++++++++++++ .../wwwroot/changelog/v0.9.55.md | 1 + 5 files changed, 65 insertions(+), 46 deletions(-) create mode 100644 app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs diff --git a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs index d29aaf45..a911f62b 100644 --- a/app/MindWork AI Studio/Components/AttachDocuments.razor.cs +++ b/app/MindWork AI Studio/Components/AttachDocuments.razor.cs @@ -1,6 +1,7 @@ using AIStudio.Dialogs; using AIStudio.Tools.Rust; using AIStudio.Tools.Services; +using AIStudio.Tools.Validation; using Microsoft.AspNetCore.Components; @@ -91,7 +92,7 @@ public partial class AttachDocuments : MSGComponentBase foreach (var path in paths) { - if(!await this.IsFileExtensionValid(path)) + if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path)) continue; this.DocumentPaths.Add(path); @@ -133,7 +134,7 @@ public partial class AttachDocuments : MSGComponentBase if (!File.Exists(selectedFile.SelectedFilePath)) return; - if (!await this.IsFileExtensionValid(selectedFile.SelectedFilePath)) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath)) return; this.DocumentPaths.Add(selectedFile.SelectedFilePath); @@ -141,30 +142,6 @@ public partial class AttachDocuments : MSGComponentBase await this.OnChange(this.DocumentPaths); } - private async Task IsFileExtensionValid(string selectedFile) - { - var ext = Path.GetExtension(selectedFile).TrimStart('.'); - if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, this.T("Executables are not allowed"))); - return false; - } - - if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, this.T("Images are not supported yet"))); - return false; - } - - if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet"))); - return false; - } - - return true; - } - private async Task ClearAllFiles() { this.DocumentPaths.Clear(); diff --git a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs index 99322ef1..cd970cd4 100644 --- a/app/MindWork AI Studio/Components/ReadFileContent.razor.cs +++ b/app/MindWork AI Studio/Components/ReadFileContent.razor.cs @@ -1,5 +1,5 @@ -using AIStudio.Tools.Rust; using AIStudio.Tools.Services; +using AIStudio.Tools.Validation; using Microsoft.AspNetCore.Components; @@ -55,28 +55,12 @@ public partial class ReadFileContent : MSGComponentBase return; } - var ext = Path.GetExtension(selectedFile.SelectedFilePath).TrimStart('.'); - if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath)) { - this.Logger.LogWarning("User attempted to load executable file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, T("Executables are not allowed"))); + this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath); return; } - if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - this.Logger.LogWarning("User attempted to load image file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, T("Images are not supported yet"))); - return; - } - - if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) - { - this.Logger.LogWarning("User attempted to load video file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext); - await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet"))); - return; - } - try { var fileContent = await UserFile.LoadFileData(selectedFile.SelectedFilePath, this.RustService, this.DialogService); diff --git a/app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs b/app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs index d755184e..71274778 100644 --- a/app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs +++ b/app/MindWork AI Studio/Tools/Rust/FileTypeFilter.cs @@ -21,7 +21,9 @@ public readonly record struct FileTypeFilter(string FilterName, string[] FilterE public static FileTypeFilter AllImages => new(TB("All Image Files"), ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "svg", "webp", "heic"]); - public static FileTypeFilter AllVideos => new(TB("All Video Files"), ["mp4", "avi", "mkv", "mov", "wmv", "flv", "webm"]); + public static FileTypeFilter AllVideos => new(TB("All Video Files"), ["mp4", "m4v", "avi", "mkv", "mov", "wmv", "flv", "webm"]); + + public static FileTypeFilter AllAudio => new(TB("All Audio Files"), ["mp3", "wav", "wave", "aac", "flac", "ogg", "m4a", "wma", "alac", "aiff", "m4b"]); public static FileTypeFilter Executables => new(TB("Executable Files"), ["exe", "app", "bin", "appimage"]); } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs new file mode 100644 index 00000000..cc40c959 --- /dev/null +++ b/app/MindWork AI Studio/Tools/Validation/FileExtensionValidation.cs @@ -0,0 +1,55 @@ +using AIStudio.Tools.PluginSystem; +using AIStudio.Tools.Rust; + +namespace AIStudio.Tools.Validation; + +/// +/// Provides centralized validation for file extensions. +/// +public static class FileExtensionValidation +{ + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileExtensionValidation).Namespace, nameof(FileExtensionValidation)); + + /// + /// Validates the file extension and sends appropriate MessageBus notifications when invalid. + /// + /// The file path to validate. + /// True if valid, false if invalid (error/warning already sent via MessageBus). + public static async Task IsExtensionValidWithNotifyAsync(string filePath) + { + var ext = Path.GetExtension(filePath).TrimStart('.'); + if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendError(new( + Icons.Material.Filled.AppBlocking, + TB("Executables are not allowed"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.ImageNotSupported, + TB("Images are not supported yet"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.FeaturedVideo, + TB("Videos are not supported yet"))); + return false; + } + + if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase))) + { + await MessageBus.INSTANCE.SendWarning(new( + Icons.Material.Filled.AudioFile, + TB("Audio files are not supported yet"))); + return false; + } + + return true; + } +} diff --git a/app/MindWork AI Studio/wwwroot/changelog/v0.9.55.md b/app/MindWork AI Studio/wwwroot/changelog/v0.9.55.md index b1cbf13b..b1cedb29 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v0.9.55.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v0.9.55.md @@ -9,6 +9,7 @@ - Improved error handling, logging, and code quality. - Improved error handling for Microsoft Word export. - Improved file reading, e.g. for the translation, summarization, and legal assistants, by performing the Pandoc validation in the first step. This prevents unnecessary selection of files that cannot be processed. +- Improved the file selection for file attachments in chat and assistant file loading by filtering out audio files. Audio attachments are not yet supported. - Fixed a bug in the local data sources info dialog (preview feature) for data directories that could cause the app to crash. The error was caused by a background thread producing data while the frontend attempted to display it. - Fixed a visual bug where a function's preview status was misaligned. You might have seen it in document analysis or the ERI server assistant. - Fixed a rare bug in the Microsoft Word export for huge documents. \ No newline at end of file