From 943431c131acc520ebacc27dbc2ad100f642ef0f Mon Sep 17 00:00:00 2001 From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com> Date: Mon, 9 Mar 2026 16:41:22 +0100 Subject: [PATCH 1/4] fix upload dialog --- AudioCuesheetEditor/Model/IO/FileUpload.cs | 25 +++++ .../Services/IO/FileInputManager.cs | 93 ++++++++++++------- .../Services/IO/IFileInputManager.cs | 28 ++++-- .../Services/IO/ImportManager.cs | 53 ++++------- AudioCuesheetEditor/Shared/AppBar.razor | 4 +- .../Shared/Cuesheet/CuesheetData.razor | 29 ++++-- .../Shared/Cuesheet/EditSections.razor | 2 +- .../Shared/Inputs/FileDropOverlay.razor | 8 +- .../Shared/Inputs/TextField.razor | 5 +- 9 files changed, 157 insertions(+), 90 deletions(-) create mode 100644 AudioCuesheetEditor/Model/IO/FileUpload.cs diff --git a/AudioCuesheetEditor/Model/IO/FileUpload.cs b/AudioCuesheetEditor/Model/IO/FileUpload.cs new file mode 100644 index 00000000..a8df1ee7 --- /dev/null +++ b/AudioCuesheetEditor/Model/IO/FileUpload.cs @@ -0,0 +1,25 @@ +//This file is part of AudioCuesheetEditor. + +//AudioCuesheetEditor is free software: you can redistribute it and/or modify +//it under the terms of the GNU General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//AudioCuesheetEditor is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU General Public License for more details. + +//You should have received a copy of the GNU General Public License +//along with Foobar. If not, see +//. +namespace AudioCuesheetEditor.Model.IO +{ + public class FileUpload(string name, string contentType, string? content = null, string? objectUrl = null) + { + public string Name { get; set; } = name; + public string ContentType { get; set; } = contentType; + public string? Content { get; set; } = content; + public string? ObjectUrl { get; set; } = objectUrl; + } +} diff --git a/AudioCuesheetEditor/Services/IO/FileInputManager.cs b/AudioCuesheetEditor/Services/IO/FileInputManager.cs index a47eb4f5..6bbabfae 100644 --- a/AudioCuesheetEditor/Services/IO/FileInputManager.cs +++ b/AudioCuesheetEditor/Services/IO/FileInputManager.cs @@ -28,12 +28,12 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg private readonly HttpClient _httpClient = httpClient; private readonly ILogger _logger = logger; - public AudioCodec? GetAudioCodec(IBrowserFile browserFile) + public AudioCodec? GetAudioCodec(string? fileContentType, string fileName) { AudioCodec? foundAudioCodec = null; - var extension = Path.GetExtension(browserFile.Name); + var extension = Path.GetExtension(fileName); // First search with mime type and file extension - var audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(browserFile.ContentType, StringComparison.OrdinalIgnoreCase) && x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); + var audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(fileContentType, StringComparison.OrdinalIgnoreCase) && x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); if (audioCodecsFound.Count() <= 1) { foundAudioCodec = audioCodecsFound.FirstOrDefault(); @@ -41,63 +41,62 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg if (foundAudioCodec == null) { // Second search with mime type or file extension - audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(browserFile.ContentType, StringComparison.OrdinalIgnoreCase) || x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); + audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(fileContentType, StringComparison.OrdinalIgnoreCase) || x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); foundAudioCodec = audioCodecsFound.FirstOrDefault(); } return foundAudioCodec; } - public bool IsValidAudiofile(IBrowserFile browserFile) + public bool IsValidAudiofile(string? fileContentType, string fileName) { - var codec = GetAudioCodec(browserFile); - return codec != null; + return GetAudioCodec(fileContentType, fileName) != null; } - public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable fileExtensions) + public bool CheckFileMimeType(string? fileContentType, string fileName, string mimeType, IEnumerable fileExtensions) { + //TODO: Tests if (_logger.IsEnabled(LogLevel.Debug)) { - _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", file.Name, file.ContentType, mimeType, fileExtensions); + _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", fileName, fileContentType, mimeType, fileExtensions); } Boolean fileMimeTypeMatches = false; - if ((file != null) && (String.IsNullOrEmpty(mimeType) == false)) + if (String.IsNullOrEmpty(mimeType) == false) { - if (String.IsNullOrEmpty(file.ContentType) == false) + if (String.IsNullOrEmpty(fileContentType) == false) { if (mimeType.EndsWith("/*")) { var mainType = mimeType[..^1]; - fileMimeTypeMatches = file.ContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase); + fileMimeTypeMatches = fileContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase); } else { - fileMimeTypeMatches = file.ContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase); + fileMimeTypeMatches = fileContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase); } } if ((fileMimeTypeMatches == false) && (fileExtensions.Any())) { //Try to find by file extension - var extension = Path.GetExtension(file.Name); + var extension = Path.GetExtension(fileName); fileMimeTypeMatches = fileExtensions.Any(x => x.Equals(extension, StringComparison.CurrentCultureIgnoreCase)); } } return fileMimeTypeMatches; } - public async Task CreateAudiofileAsync(String? fileInputId, IBrowserFile? browserFile, Action>? afterContentStreamLoaded = null) + public async Task CreateAudiofileAsync(FileUpload fileUpload, Action>? afterContentStreamLoaded = null) { Audiofile? audiofile = null; - if ((String.IsNullOrEmpty(fileInputId) == false) && (browserFile != null)) + if (fileUpload.ObjectUrl != null) { // Check file mime type - var codec = GetAudioCodec(browserFile); + var codec = GetAudioCodec(fileUpload.ContentType, fileUpload.Name); if (codec != null) { - var audioFileObjectURL = await _jsRuntime.InvokeAsync("getObjectURLFromMudFileUpload", fileInputId); - audiofile = new Audiofile(browserFile.Name, audioFileObjectURL, codec); - if (String.IsNullOrEmpty(audioFileObjectURL) == false) + audiofile = new Audiofile(fileUpload.Name, fileUpload.ObjectUrl, codec); + if (String.IsNullOrEmpty(fileUpload.ObjectUrl) == false) { - var request = new HttpRequestMessage(HttpMethod.Get, audioFileObjectURL); + var request = new HttpRequestMessage(HttpMethod.Get, fileUpload.ObjectUrl); //TODO: Enable when https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/issues/524 gets done request.SetBrowserRequestStreamingEnabled(false); @@ -119,27 +118,25 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable return audiofile; } - public CDTextfile? CreateCDTextfile(IBrowserFile? browserFile) + public CDTextfile? CreateCDTextfile(string? fileContentType, string fileName) { - CDTextfile? cdTextfile = null; - if (browserFile != null) + CDTextfile? cdTextfile; + if (CheckFileMimeType(fileContentType, fileName, FileMimeTypes.Text, [FileExtensions.CDTextfile])) { - if (CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.CDTextfile])) - { - cdTextfile = new CDTextfile(browserFile.Name); - } - else - { - throw new ArgumentException("The cdtextfile provided is not of a valid type."); - } + cdTextfile = new CDTextfile(fileName); + } + else + { + throw new ArgumentException("The cdtextfile provided is not of a valid type."); } return cdTextfile; } /// - public bool IsValidForImportView(IBrowserFile browserFile) + public bool IsValidForImportView(string? fileContentType, string fileName) { - return CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]); + //TODO: Tests + return CheckFileMimeType(fileContentType, fileName, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]); } /// @@ -148,5 +145,33 @@ public async Task ReadFileContentAsync(IBrowserFile browserFile) var fileContent = new StreamContent(browserFile.OpenReadStream()); return await fileContent.ReadAsStringAsync(); } + + /// + public async Task> CreateFileUploadsAsync(IReadOnlyList browserFiles, string? fileInputId = null) + { + //TODO: Tests + List fileUploads = []; + foreach (var file in browserFiles) + { + if (CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]) + || CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]) + || IsValidForImportView(file.ContentType, file.Name) + || IsValidAudiofile(file.ContentType, file.Name)) + { + string? content = null; + string? objectUrl = null; + if (IsValidAudiofile(file.ContentType, file.Name)) + { + objectUrl = await _jsRuntime.InvokeAsync("getObjectURLFromMudFileUpload", fileInputId); + } + else + { + content = await ReadFileContentAsync(file); + } + fileUploads.Add(new(file.Name, file.ContentType, content, objectUrl)); + } + } + return fileUploads; + } } } diff --git a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs index e5d00d13..026ed5d4 100644 --- a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs +++ b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs @@ -15,6 +15,7 @@ //. using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using Microsoft.AspNetCore.Components.Forms; @@ -22,22 +23,37 @@ namespace AudioCuesheetEditor.Services.IO { public interface IFileInputManager { - bool IsValidAudiofile(IBrowserFile browserFile); - AudioCodec? GetAudioCodec(IBrowserFile browserFile); - bool CheckFileMimeType(IBrowserFile file, string mimeType, IEnumerable fileExtensions); - Task CreateAudiofileAsync(string? fileInputId, IBrowserFile? browserFile, Action>? afterContentStreamLoaded = null); - CDTextfile? CreateCDTextfile(IBrowserFile? browserFile); + bool IsValidAudiofile(string? fileContentType, string fileName); + AudioCodec? GetAudioCodec(string? fileContentType, string fileName); + /// + /// Checks if a file content type and name matches given parameters + /// + /// + /// + /// + /// + /// + bool CheckFileMimeType(string? fileContentType, string fileName, string mimeType, IEnumerable fileExtensions); + Task CreateAudiofileAsync(FileUpload fileUpload, Action>? afterContentStreamLoaded = null); + CDTextfile? CreateCDTextfile(string? fileContentType, string fileName); /// /// Checks if the file can be used for the import view /// /// /// - bool IsValidForImportView(IBrowserFile browserFile); + bool IsValidForImportView(string? fileContentType, string fileName); /// /// Reads the browser file and gets the file content as string /// /// /// Task ReadFileContentAsync(IBrowserFile browserFile); + /// + /// Generates file upload references for files + /// + /// + /// + /// + Task> CreateFileUploadsAsync(IReadOnlyList browserFiles, string? fileInputId = null); } } \ No newline at end of file diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs index 15a32fb3..6817670f 100644 --- a/AudioCuesheetEditor/Services/IO/ImportManager.cs +++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs @@ -121,59 +121,47 @@ public void ImportCuesheet() } } - public async Task UploadFilesAsync(IEnumerable files, String? fileInputId = null) + public async Task UploadFilesAsync(IEnumerable files) { var stopwatch = Stopwatch.StartNew(); var invalidFiles = new List(); foreach (var file in files) { - if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]) - || _fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]) - || _fileInputManager.IsValidForImportView(file) - || _fileInputManager.IsValidAudiofile(file)) + if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]) + || _fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]) + || _fileInputManager.IsValidForImportView(file.ContentType, file.Name) + || _fileInputManager.IsValidAudiofile(file.ContentType, file.Name)) { - if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])) + if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])) { - var fileContent = await ReadFileContentAsync(file); - fileContent.Position = 0; - using var reader = new StreamReader(fileContent); - var stringFileContent = reader.ReadToEnd(); _sessionStateContainer.Importfile = new Importfile() { - FileContent = stringFileContent, - FileContentRecognized = stringFileContent, + FileContent = file.Content, + FileContentRecognized = file.Content, FileType = ImportFileType.ProjectFile }; } - if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])) + if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])) { - var fileContent = await ReadFileContentAsync(file); - fileContent.Position = 0; - using var reader = new StreamReader(fileContent); - var stringFileContent = reader.ReadToEnd(); _sessionStateContainer.Importfile = new Importfile() { - FileContent = stringFileContent, - FileContentRecognized = stringFileContent, + FileContent = file.Content, + FileContentRecognized = file.Content, FileType = ImportFileType.Cuesheet }; } - if (_fileInputManager.IsValidForImportView(file)) + if (_fileInputManager.IsValidForImportView(file.ContentType, file.Name)) { - var fileContent = await ReadFileContentAsync(file); - fileContent.Position = 0; - using var reader = new StreamReader(fileContent); - var stringFileContent = reader.ReadToEnd(); _sessionStateContainer.Importfile = new Importfile() { - FileContent = stringFileContent, - FileContentRecognized = stringFileContent, + FileContent = file.Content, + FileContentRecognized = file.Content, FileType = ImportFileType.Textfile }; } - if (_fileInputManager.IsValidAudiofile(file)) + if (_fileInputManager.IsValidAudiofile(file.ContentType, file.Name)) { - var audioFile = await _fileInputManager.CreateAudiofileAsync(fileInputId, file); + var audioFile = await _fileInputManager.CreateAudiofileAsync(file); _sessionStateContainer.ImportAudiofile = audioFile; } } @@ -190,15 +178,6 @@ public async Task UploadFilesAsync(IEnumerable files, String? file } } - private static async Task ReadFileContentAsync(IBrowserFile file) - { - var fileContent = new MemoryStream(); - var stream = file.OpenReadStream(); - await stream.CopyToAsync(fileContent); - stream.Close(); - return fileContent; - } - private static void CopyCuesheet(Cuesheet target, ICuesheet cuesheetToCopy) { target.IsImporting = true; diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor index d4f89c8c..395d5e3d 100644 --- a/AudioCuesheetEditor/Shared/AppBar.razor +++ b/AudioCuesheetEditor/Shared/AppBar.razor @@ -25,6 +25,7 @@ along with Foobar. If not, see @inject IJSRuntime _jsRuntime @inject HotKeys _hotKeys @inject ImportManager _importManager +@inject IFileInputManager _fileInputManager @@ -229,7 +230,8 @@ along with Foobar. If not, see async Task FileUploaded(IBrowserFile file) { - await _importManager.UploadFilesAsync([file]); + var fileUploads = await _fileInputManager.CreateFileUploadsAsync([file]); + await _importManager.UploadFilesAsync(fileUploads); } async Task ShowHotkeysDialog() diff --git a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor index 4d44b0a3..61605a5a 100644 --- a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor +++ b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor @@ -111,14 +111,22 @@ along with Foobar. If not, see fileInputAudiofileErrorText = null; try { - Cuesheet.Audiofile = await _fileInputManager.CreateAudiofileAsync(fileInputAudiofileId, browserFile, x => + if (browserFile != null) { - if (Cuesheet.RecalculateLastTrackEnd()) + var fileUpload = await _fileInputManager.CreateFileUploadsAsync([browserFile], fileInputAudiofileId); + Cuesheet.Audiofile = await _fileInputManager.CreateAudiofileAsync(fileUpload.Single(), x => { - TraceChangeManager.MergeLastEditWithEdit(x => x.Changes.All(y => y.TraceableObject == Cuesheet && y.TraceableChange.PropertyName == nameof(Audiofile))); - } - StateHasChanged(); - }); + if (Cuesheet.RecalculateLastTrackEnd()) + { + TraceChangeManager.MergeLastEditWithEdit(x => x.Changes.All(y => y.TraceableObject == Cuesheet && y.TraceableChange.PropertyName == nameof(Audiofile))); + } + StateHasChanged(); + }); + } + else + { + Cuesheet.Audiofile = null; + } } catch(ArgumentException ae) { @@ -157,7 +165,14 @@ along with Foobar. If not, see fileInputCDTextfileErrorText = null; try { - Cuesheet.CDTextfile = _fileInputManager.CreateCDTextfile(browserFile); + if (browserFile != null) + { + Cuesheet.CDTextfile = _fileInputManager.CreateCDTextfile(browserFile.ContentType, browserFile.Name); + } + else + { + Cuesheet.CDTextfile = null; + } } catch (ArgumentException ae) { diff --git a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor index d1abd5cb..2c998de5 100644 --- a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor +++ b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor @@ -142,7 +142,7 @@ along with Foobar. If not, see void AudiofileSelected(CuesheetSection section, IBrowserFile? browserFile) { - if ((browserFile != null) && (_fileInputManager.IsValidAudiofile(browserFile) == true)) + if ((browserFile != null) && (_fileInputManager.IsValidAudiofile(browserFile.ContentType, browserFile.Name) == true)) { section.AudiofileName = browserFile?.Name; } diff --git a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor index 2f22ee2f..deb044b1 100644 --- a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor +++ b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor @@ -23,6 +23,7 @@ along with Foobar. If not, see @inject IStringLocalizer _localizer @inject ImportManager _importManager @inject DialogManager _dialogManager +@inject IFileInputManager _fileInputManager