diff --git a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs index f78b881c..4e4de503 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs @@ -20,7 +20,13 @@ using Microsoft.JSInterop; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Net.Http; +using System.Text; +using System.Threading.Tasks; namespace AudioCuesheetEditor.Tests.Services.IO { @@ -34,11 +40,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesNotMatchButExtensio var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.mp3", "audio/wav"); + var fileName = "test.mp3"; + var contentType = "audio/wav"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3"]); + var result = manager.CheckFileMimeType(contentType, fileName, "audio/mpeg", [".mp3"]); // Assert Assert.IsTrue(result); @@ -51,11 +58,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesMatchButNotExtensio var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.mpeg", "audio/mpeg"); + var fileName = "test.mpeg"; + var contentType = "audio/mpeg"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3", ".txt"]); + var result = manager.CheckFileMimeType(contentType, fileName, "audio/mpeg", [".mp3", ".txt"]); // Assert Assert.IsTrue(result); @@ -68,11 +76,12 @@ public void CheckFileMimeType_ReturnsFalse_WhenExtensionDoesNotMatchAndContentTy var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.flac", string.Empty); + var fileName = "test.flac"; + var contentType = string.Empty; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/flac", [".mp3"]); + var result = manager.CheckFileMimeType(contentType, fileName, "audio/flac", [".mp3"]); // Assert Assert.IsFalse(result); @@ -85,11 +94,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeAndExtensionMatch() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.wav", "audio/wave"); + var fileName = "test.wav"; + var contentType = "audio/wave"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "audio/wave", [".wav"]); + var result = manager.CheckFileMimeType(contentType, fileName, "audio/wave", [".wav"]); // Assert Assert.IsTrue(result); @@ -102,11 +112,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentMainTypeMatch() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("history.txt", "text/plain"); + var fileName = "history.txt"; + var contentType = "text/plain"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.CheckFileMimeType(file, "text/*", [".txt", ".text"]); + var result = manager.CheckFileMimeType(contentType, fileName, "text/*", [".txt", ".text"]); // Assert Assert.IsTrue(result); @@ -119,11 +130,12 @@ public void IsValidAudiofile_ReturnsTrue_WithValidAudiocodec() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.wav", "audio/wav"); + var fileName = "test.wav"; + var contentType = "audio/wav"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.IsValidAudiofile(file); + var result = manager.IsValidAudiofile(contentType, fileName); // Assert Assert.IsTrue(result); @@ -136,11 +148,12 @@ public void IsValidAudiofile_ReturnsFalse_WithInvalidAudiocodecAndExtension() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.mock", "just a fantasy"); + var fileName = "test.mock"; + var contentType = "just a fantasy"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.IsValidAudiofile(file); + var result = manager.IsValidAudiofile(contentType, fileName); // Assert Assert.IsFalse(result); @@ -153,11 +166,12 @@ public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeMatches() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.wbem", "audio/webm"); + var fileName = "test.wbem"; + var contentType = "audio/webm"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.GetAudioCodec(file); + var result = manager.GetAudioCodec(contentType, fileName); // Assert Assert.IsNotNull(result); @@ -171,11 +185,12 @@ public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeAndFileExtensionMatch var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.webm", "audio/webm"); + var fileName = "test.wbem"; + var contentType = "audio/webm"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.GetAudioCodec(file); + var result = manager.GetAudioCodec(contentType, fileName); // Assert Assert.IsNotNull(result); @@ -189,11 +204,12 @@ public void GetAudioCodec_ReturnsNull_WhenContentTypeAndFileExtensionNotMatch() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.acx", "fantasy stuff"); + var fileName = "test.acx"; + var contentType = "fantasy stuff"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.GetAudioCodec(file); + var result = manager.GetAudioCodec(contentType, fileName); // Assert Assert.IsNull(result); @@ -206,11 +222,12 @@ public void IsValidForImportView_ReturnsTrue_WhenFileIsHtml() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.html", "text/html"); + var fileName = "test.html"; + var contentType = "text/html"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.IsValidForImportView(file); + var result = manager.IsValidForImportView(contentType, fileName); // Assert Assert.IsTrue(result); @@ -223,21 +240,76 @@ public void IsValidForImportView_ReturnsFalse_WhenFileIsBinary() var jsRuntimeMock = new Mock(); var httpClientMock = new Mock(); var loggerMock = new Mock>(); - var file = CreateBrowserFile("test.dat", "application/octet-stream"); + var fileName = "test.dat"; + var contentType = "application/octet-stream"; var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); // Act - var result = manager.IsValidForImportView(file); + var result = manager.IsValidForImportView(contentType, fileName); // Assert Assert.IsFalse(result); } - private static IBrowserFile CreateBrowserFile(string name, string contentType) + [TestMethod] + public async Task CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextContentAsync() + { + // Arrange + var jsRuntimeMock = new Mock(); + var httpClientMock = new Mock(); + var loggerMock = new Mock>(); + var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); + var firstFile = CreateBrowserFile("Test.txt", "text/plain", "Just a test!"); + var secondFile = CreateBrowserFile("Test.mp3", "audio/mpeg"); + var fileInputId = nameof(CreateFileUploadsAsync_ReturnsFileUploads_WhenFileHasTextContentAsync); + IReadOnlyList browserfiles = [ + firstFile, + secondFile + ]; + var objectUrl = "Some object url!"; + jsRuntimeMock.Setup(js => js.InvokeAsync(It.IsAny(), It.IsAny())).ReturnsAsync(objectUrl); + // Act + var result = await manager.CreateFileUploadsAsync(browserfiles, fileInputId); + // Assert + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(firstFile.Name, result.First().Name); + Assert.AreEqual(firstFile.ContentType, result.First().ContentType); + Assert.AreEqual("Just a test!", result.First().Content); + Assert.AreEqual(secondFile.Name, result.Last().Name); + Assert.AreEqual(secondFile.ContentType, result.Last().ContentType); + Assert.IsNull(result.Last().Content); + Assert.AreEqual(objectUrl, result.Last().ObjectUrl); + } + + [TestMethod] + public async Task CreateFileUploadsAsync_ReturnsEmpty_WhenFilesHaveInvalidMimeTypeAsync() + { + // Arrange + var jsRuntimeMock = new Mock(); + var httpClientMock = new Mock(); + var loggerMock = new Mock>(); + var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object); + var firstFile = CreateBrowserFile("Test.bin", "binary", "Just a test!"); + var secondFile = CreateBrowserFile("Test.bin", "octet/stream"); + IReadOnlyList browserfiles = [ + firstFile, + secondFile + ]; + // Act + var result = await manager.CreateFileUploadsAsync(browserfiles); + // Assert + Assert.AreEqual(0, result.Count()); + } + + private static IBrowserFile CreateBrowserFile(string name, string contentType, string? content = null) { var fileMock = new Mock(); fileMock.Setup(f => f.Name).Returns(name); fileMock.Setup(f => f.ContentType).Returns(contentType); + if (content != null) + { + fileMock.Setup(f => f.OpenReadStream()).Returns(new MemoryStream(Encoding.UTF8.GetBytes(content))); + } return fileMock.Object; } } diff --git a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs index a1481c15..5b6b2ec9 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs @@ -215,10 +215,10 @@ public async Task UploadFilesAsync_ProjectFile_ImportsCorrectly() { // Arrange var fileContent = "This is the content"; - var file = CreateBrowserFileMock("test.projectfile", fileContent); - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(true); - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false); + var file = new FileUpload("test.projectfile", "text/plain", fileContent); + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(true); + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(false); IImportfile? sessionStateContainerImportfile = null; _sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x); @@ -237,11 +237,11 @@ public async Task UploadFilesAsync_CuesheetFile_ImportsCorrectly() { // Arrange var fileContent = "Cuesheet file content"; - var file = CreateBrowserFileMock("test.cue", fileContent); - - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(true); - _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false); + var file = new FileUpload("test.cue", "text/plain", fileContent); + + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(true); + _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(false); IImportfile? sessionStateContainerImportfile = null; _sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x); @@ -260,11 +260,11 @@ public async Task UploadFilesAsync_TextFile_ImportsCorrectly() { // Arrange var fileContent = "TextFileContent"; - var file = CreateBrowserFileMock("test.txt", fileContent); - - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(true); + var file = new FileUpload("test.txt", "text/plain", fileContent); + + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(true); IImportfile? sessionStateContainerImportfile = null; _sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x); @@ -282,13 +282,13 @@ public async Task UploadFilesAsync_TextFile_ImportsCorrectly() public async Task UploadFilesAsync_WithAudiofile_ImportsCorrectly() { // Arrange - var file = CreateBrowserFileMock("test.mp3"); - - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); - _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false); - _fileInputManagerMock.Setup(f => f.IsValidAudiofile(file)).Returns(true); - _fileInputManagerMock.Setup(f => f.CreateAudiofileAsync(It.IsAny(), It.IsAny(), It.IsAny>?>())).ReturnsAsync(new AudioCuesheetEditor.Model.IO.Audio.Audiofile(file.Name)); + var file = new FileUpload("test.mp3", "audio/mpeg"); + + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false); + _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(false); + _fileInputManagerMock.Setup(f => f.IsValidAudiofile(file.ContentType, file.Name)).Returns(true); + _fileInputManagerMock.Setup(f => f.CreateAudiofileAsync(It.IsAny(), It.IsAny>?>())).ReturnsAsync(new AudioCuesheetEditor.Model.IO.Audio.Audiofile(file.Name)); IImportfile? sessionStateContainerImportfile = null; _sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x); @@ -359,14 +359,5 @@ public void ImportData_ValidData_SetsImportfile() Assert.AreEqual(importData, sessionStateContainerImportfile.FileContentRecognized); Assert.AreEqual(ImportFileType.Textfile, sessionStateContainerImportfile.FileType); } - - private static IBrowserFile CreateBrowserFileMock(string name, string content = "TestContent") - { - var fileMock = new Mock(); - fileMock.Setup(f => f.Name).Returns(name); - fileMock.Setup(f => f.OpenReadStream(It.IsAny(), It.IsAny())) - .Returns(new MemoryStream(Encoding.UTF8.GetBytes(content))); - return fileMock.Object; - } } } \ No newline at end of file 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..61cef4db 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,61 @@ 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) { 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())) + 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 +117,24 @@ 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]); + return CheckFileMimeType(fileContentType, fileName, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]); } /// @@ -148,5 +143,32 @@ 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) + { + 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