diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj index 7bb5869e..f0b6f137 100644 --- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj +++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj @@ -53,6 +53,8 @@ + + @@ -98,6 +100,8 @@ + + diff --git a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs new file mode 100644 index 00000000..938d0b4c --- /dev/null +++ b/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs @@ -0,0 +1,28 @@ +//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 +//. +using AudioCuesheetEditor.Model.Options; +using System.Linq.Expressions; + +namespace AudioCuesheetEditor.Data.Options +{ + public interface ILocalStorageOptionsProvider + { + event EventHandler? OptionSaved; + Task GetOptions() where T : IOptions; + Task SaveOptions(IOptions options); + Task SaveOptionsValue(Expression> propertyExpression, object value) where T : class, IOptions, new(); + } +} diff --git a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs index 62cd9f52..743a4cf1 100644 --- a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs +++ b/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs @@ -22,7 +22,7 @@ namespace AudioCuesheetEditor.Data.Options { - public class LocalStorageOptionsProvider(IJSRuntime jsRuntime) + public class LocalStorageOptionsProvider(IJSRuntime jsRuntime): ILocalStorageOptionsProvider { public event EventHandler? OptionSaved; diff --git a/AudioCuesheetEditor/Extensions/SessionStateContainer.cs b/AudioCuesheetEditor/Extensions/SessionStateContainer.cs index cbc900d4..1a988975 100644 --- a/AudioCuesheetEditor/Extensions/SessionStateContainer.cs +++ b/AudioCuesheetEditor/Extensions/SessionStateContainer.cs @@ -31,8 +31,6 @@ public class SessionStateContainer private ViewMode currentViewMode; private Cuesheet cuesheet; private Cuesheet? importCuesheet; - private TextImportfile? textImportFile; - private CuesheetImportfile? cuesheetImportFile; private Audiofile? importAudiofile; public SessionStateContainer(TraceChangeManager traceChangeManager) @@ -65,50 +63,6 @@ public Cuesheet? ImportCuesheet } } - public TextImportfile? TextImportFile - { - get { return textImportFile; } - set - { - if (textImportFile != null) - { - textImportFile.AnalysisFinished -= TextImportScheme_AnalysisFinished; - } - textImportFile = value; - if (textImportFile != null) - { - textImportFile.AnalysisFinished += TextImportScheme_AnalysisFinished; - ImportCuesheet = textImportFile.Cuesheet; - } - else - { - ImportCuesheet = null; - } - } - } - - public CuesheetImportfile? CuesheetImportFile - { - get { return cuesheetImportFile; } - set - { - if (cuesheetImportFile != null) - { - cuesheetImportFile.AnalysisFinished -= CuesheetImportFile_AnalysisFinished; - } - cuesheetImportFile = value; - if ((CuesheetImportFile != null) && (CuesheetImportFile.Cuesheet != null)) - { - CuesheetImportFile.AnalysisFinished += CuesheetImportFile_AnalysisFinished; - ImportCuesheet = CuesheetImportFile.Cuesheet; - } - else - { - ImportCuesheet = null; - } - } - } - public Audiofile? ImportAudiofile { get => importAudiofile; @@ -133,74 +87,26 @@ public ViewMode CurrentViewMode } } - public IImportfile? Importfile - { - get - { - if (TextImportFile != null) - { - return TextImportFile; - } - if (CuesheetImportFile != null) - { - return CuesheetImportFile; - } - return null; - } - } + public IImportfile? Importfile{ get; set; } public void ResetImport() { - TextImportFile = null; - CuesheetImportFile = null; + Importfile = null; ImportAudiofile = null; - } - - public void StartImportCuesheet(ApplicationOptions applicationOptions) - { - if (ImportCuesheet != null) - { - Cuesheet.Import(ImportCuesheet, applicationOptions, _traceChangeManager); - ImportCuesheet = null; - } - ResetImport(); + ImportCuesheet = null; } private void SetCuesheetReference(Cuesheet value) { - cuesheet.CuesheetImported -= Cuesheet_CuesheetImported; cuesheet = value; - cuesheet.CuesheetImported += Cuesheet_CuesheetImported; _traceChangeManager.Reset(); _traceChangeManager.TraceChanges(Cuesheet); CuesheetChanged?.Invoke(this, EventArgs.Empty); } - private void TextImportScheme_AnalysisFinished(object? sender, EventArgs eventArgs) - { - if (textImportFile != null) - { - ImportCuesheet = textImportFile.Cuesheet; - } - else - { - ImportCuesheet = null; - } - } - private void Cuesheet_CuesheetImported(object? sender, EventArgs e) + + public void FireCuesheetImported() { CuesheetChanged?.Invoke(this, EventArgs.Empty); } - - void CuesheetImportFile_AnalysisFinished(object? sender, EventArgs e) - { - if (CuesheetImportFile != null) - { - ImportCuesheet = CuesheetImportFile.Cuesheet; - } - else - { - ImportCuesheet = null; - } - } } } diff --git a/AudioCuesheetEditor/Extensions/WebAssemblyHostExtension.cs b/AudioCuesheetEditor/Extensions/WebAssemblyHostExtension.cs index 0c68e392..bdba0ae4 100644 --- a/AudioCuesheetEditor/Extensions/WebAssemblyHostExtension.cs +++ b/AudioCuesheetEditor/Extensions/WebAssemblyHostExtension.cs @@ -24,7 +24,7 @@ public static class WebAssemblyHostExtension { public async static Task SetDefaultCulture(this WebAssemblyHost host) { - var localStorageOptionsProvider = host.Services.GetRequiredService(); + var localStorageOptionsProvider = host.Services.GetRequiredService(); var options = await localStorageOptionsProvider.GetOptions(); CultureInfo.DefaultThreadCurrentCulture = options.Culture; diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/CatalogueNumber.cs b/AudioCuesheetEditor/Model/AudioCuesheet/CatalogueNumber.cs index fa38b21f..41c35d76 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/CatalogueNumber.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/CatalogueNumber.cs @@ -46,12 +46,12 @@ protected override ValidationResult Validate(string property) validationStatus = ValidationStatus.Success; if (Value.All(Char.IsDigit) == false) { - validationMessages ??= new(); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("{0} must only contain numbers!", nameof(Value))); } if (Value.Length != 13) { - validationMessages ??= new(); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("{0} has an invalid length. Allowed length is {1}!", nameof(Value), 13)); } } diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs index 4b17102c..755f4d14 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Cuesheet.cs @@ -14,7 +14,6 @@ //along with Foobar. If not, see //. using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Export; using AudioCuesheetEditor.Model.Options; @@ -39,11 +38,8 @@ public class CuesheetSectionAddRemoveEventArgs(CuesheetSection section) : EventA public CuesheetSection Section { get; } = section; } - public class Cuesheet(TraceChangeManager? traceChangeManager = null) : Validateable, ICuesheetEntity, ITraceable + public class Cuesheet(TraceChangeManager? traceChangeManager = null) : Validateable, ITraceable, ICuesheet { - public const String MimeType = "text/*"; - public const String FileExtension = ".cue"; - private readonly object syncLock = new(); private List tracks = []; @@ -62,7 +58,6 @@ public class Cuesheet(TraceChangeManager? traceChangeManager = null) : Validatea public event EventHandler? TrackRemoved; public event EventHandler? SectionAdded; public event EventHandler? SectionRemoved; - public event EventHandler? CuesheetImported; [JsonInclude] public IReadOnlyCollection Tracks @@ -176,7 +171,7 @@ public TimeSpan? RecordingTime } [JsonIgnore] - public Boolean IsImporting { get; private set; } + public Boolean IsImporting { get; set; } [JsonInclude] public IReadOnlyCollection Sections @@ -393,27 +388,6 @@ public void MoveTrack(Track track, MoveDirection moveDirection) } } - public void Import(Cuesheet cuesheet, ApplicationOptions applicationOptions, TraceChangeManager? traceChangeManager = null) - { - //Since we use a stack for several changes we need to lock execution for everything else - lock (syncLock) - { - IsImporting = true; - //We are doing a bulk edit, so inform the TraceChangeManager - if (traceChangeManager != null) - { - traceChangeManager.BulkEdit = true; - } - CopyValues(cuesheet, applicationOptions); - if (traceChangeManager != null) - { - traceChangeManager.BulkEdit = false; - } - IsImporting = false; - } - CuesheetImported?.Invoke(this, EventArgs.Empty); - } - public void StartRecording() { recordingStart = DateTime.UtcNow; @@ -554,32 +528,6 @@ private void ReCalculateTrackProperties(Track trackToCalculate) } } - /// - /// Copy values from import cuesheet to this cuesheet - /// - /// Reference to import cuesheet - /// Reference to application options - private void CopyValues(Cuesheet cuesheet, ApplicationOptions applicationOptions) - { - Artist = cuesheet.Artist; - Title = cuesheet.Title; - Audiofile = cuesheet.Audiofile; - CDTextfile = cuesheet.CDTextfile; - Cataloguenumber = cuesheet.Cataloguenumber; - foreach (var importTrack in cuesheet.Tracks) - { - //We don't want to copy the cuesheet reference since we are doing a copy and want to assign the track to this object - var track = new Track(importTrack, false); - AddTrack(track, applicationOptions); - } - // Copy sections - foreach (var splitPoint in cuesheet.Sections) - { - var newSplitPoint = AddSection(); - newSplitPoint.CopyValues(splitPoint); - } - } - private void Track_RankPropertyValueChanged(object? sender, string e) { if (sender is Track trackRaisedEvent) diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/ICuesheetEntity.cs b/AudioCuesheetEditor/Model/AudioCuesheet/ICuesheet.cs similarity index 70% rename from AudioCuesheetEditor/Model/AudioCuesheet/ICuesheetEntity.cs rename to AudioCuesheetEditor/Model/AudioCuesheet/ICuesheet.cs index 4e0b86f4..02a2e007 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/ICuesheetEntity.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/ICuesheet.cs @@ -13,19 +13,12 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace AudioCuesheetEditor.Model.AudioCuesheet { - /// - /// Interface for cuesheet entities (Cuesheet, track, etc.) - /// - public interface ICuesheetEntity + public interface ICuesheet { - public String? Artist { get; set; } - public String? Title { get; set; } + string? Artist { get; set; } + string? Title { get; set; } } } diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs b/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs new file mode 100644 index 00000000..6b0d6192 --- /dev/null +++ b/AudioCuesheetEditor/Model/AudioCuesheet/ITrack.cs @@ -0,0 +1,31 @@ +//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.AudioCuesheet +{ + public interface ITrack + { + string? Artist { get; set; } + string? Title { get; set; } + uint? Position { get; set; } + TimeSpan? Begin { get; set; } + TimeSpan? End { get; set; } + TimeSpan? Length { get; set; } + IReadOnlyCollection Flags { get; } + TimeSpan? PreGap { get; set; } + TimeSpan? PostGap { get; set; } + void SetFlags(IEnumerable flags); + } +} diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportCuesheet.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportCuesheet.cs new file mode 100644 index 00000000..053228b6 --- /dev/null +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportCuesheet.cs @@ -0,0 +1,30 @@ +//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.AudioCuesheet.Import +{ + public class ImportCuesheet : ICuesheet + { + public string? Artist { get; set; } + public string? Title { get; set; } + public string? Audiofile { get;set; } + /// + public string? CDTextfile { get; set; } + public string? Cataloguenumber { get; set; } + public ICollection Tracks { get; } = []; + } +} diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs new file mode 100644 index 00000000..88f9033d --- /dev/null +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Import/ImportTrack.cs @@ -0,0 +1,38 @@ +//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.AudioCuesheet.Import +{ + public class ImportTrack : ITrack + { + private readonly List flags = []; + public string? Artist { get; set; } + public string? Title { get; set; } + public uint? Position { get; set; } + public TimeSpan? Begin { get; set; } + public TimeSpan? End { get; set; } + public TimeSpan? Length { get; set; } + public IReadOnlyCollection Flags => flags; + public TimeSpan? PreGap { get; set; } + public TimeSpan? PostGap { get; set; } + public DateTime? StartDateTime { get; set; } + public void SetFlags(IEnumerable flags) + { + this.flags.Clear(); + this.flags.AddRange(flags); + } + } +} diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs index 7e67d2c2..24353e82 100644 --- a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs +++ b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs @@ -25,7 +25,7 @@ public enum SetFlagMode Remove } - public class Track : Validateable, ICuesheetEntity, ITraceable + public class Track : Validateable, ITraceable, ITrack { public static readonly List AllPropertyNames = [nameof(IsLinkedToPreviousTrack), nameof(Position), nameof(Artist), nameof(Title), nameof(Begin), nameof(End), nameof(Flags), nameof(PreGap), nameof(PostGap), nameof(Length)]; @@ -54,19 +54,18 @@ public class Track : Validateable, ICuesheetEntity, ITraceable /// public event EventHandler? TraceablePropertyChanged; + public Track() { } + /// /// Create object with copied values from input /// /// Object to copy values from /// /// Copy cuesheet reference from track also? - public Track(Track track, Boolean copyCuesheetReference = true) + public Track(ITrack track, Boolean copyCuesheetReference = true) : this() { CopyValues(track, copyCuesheetReference); } - public Track() - { - - } + public uint? Position { get => position; @@ -242,17 +241,17 @@ public Track Clone() /// Copy PreGap from track also? /// Copy PostGap from track also? /// A list of properties, for whom the internal set construct should be used, in order to avoid automatic calculation. - public void CopyValues(Track track, Boolean setCuesheet = true, Boolean setIsLinkedToPreviousTrack = true, Boolean setPosition = true, Boolean setArtist = true, Boolean setTitle = true, Boolean setBegin = true, Boolean setEnd = true, Boolean setLength = false, Boolean setFlags = true, Boolean setPreGap = true, Boolean setPostGap = true, IEnumerable? useInternalSetters = null) + public void CopyValues(ITrack track, Boolean setCuesheet = true, Boolean setIsLinkedToPreviousTrack = true, Boolean setPosition = true, Boolean setArtist = true, Boolean setTitle = true, Boolean setBegin = true, Boolean setEnd = true, Boolean setLength = false, Boolean setFlags = true, Boolean setPreGap = true, Boolean setPostGap = true, IEnumerable? useInternalSetters = null) { - if (setCuesheet) + if (setCuesheet && (track is Track cuesheetTrack)) { if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(Cuesheet)))) { - cuesheet = track.Cuesheet; + cuesheet = cuesheetTrack.Cuesheet; } else { - Cuesheet = track.Cuesheet; + Cuesheet = cuesheetTrack.Cuesheet; } } if (setPosition) @@ -348,15 +347,15 @@ public void CopyValues(Track track, Boolean setCuesheet = true, Boolean setIsLin PostGap = track.PostGap; } } - if (setIsLinkedToPreviousTrack) + if (setIsLinkedToPreviousTrack && (track is Track cuesheetTrack2)) { if ((useInternalSetters != null) && (useInternalSetters.Contains(nameof(IsLinkedToPreviousTrack)))) { - isLinkedToPreviousTrack = track.IsLinkedToPreviousTrack; + isLinkedToPreviousTrack = cuesheetTrack2.IsLinkedToPreviousTrack; } else { - IsLinkedToPreviousTrack = track.IsLinkedToPreviousTrack; + IsLinkedToPreviousTrack = cuesheetTrack2.IsLinkedToPreviousTrack; } } } diff --git a/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs b/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs index a390feda..67568106 100644 --- a/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs +++ b/AudioCuesheetEditor/Model/IO/Audio/Audiofile.cs @@ -17,13 +17,14 @@ namespace AudioCuesheetEditor.Model.IO.Audio { - public class Audiofile : IDisposable + [method: JsonConstructor] + public class Audiofile(String name, Boolean isRecorded = false) : IDisposable { public static readonly String RecordingFileName = "Recording.webm"; public static readonly AudioCodec AudioCodecWEBM = new("audio/webm", ".webm", "AudioCodec WEBM"); - public static readonly List AudioCodecs = new() - { + public static readonly List AudioCodecs = + [ AudioCodecWEBM, new AudioCodec("audio/mpeg", ".mp3", "AudioCodec MP3"), new AudioCodec("audio/ogg", ".oga", "AudioCodec OGA"), @@ -32,19 +33,12 @@ public class Audiofile : IDisposable new AudioCodec("audio/wav", ".wav", "AudioCodec WAV"), new AudioCodec("audio/wav", ".wave", "AudioCodec WAVE"), new AudioCodec("audio/flac", ".flac", "AudioCodec FLAC") - }; + ]; private AudioCodec? audioCodec; private bool disposedValue; public event EventHandler? ContentStreamLoaded; - - [JsonConstructor] - public Audiofile(String name, Boolean isRecorded = false) - { - Name = name; - IsRecorded = isRecorded; - } public Audiofile(String name, String objectURL, AudioCodec? audioCodec, HttpClient httpClient, Boolean isRecorded = false) : this(name, isRecorded) { @@ -57,7 +51,7 @@ public Audiofile(String name, String objectURL, AudioCodec? audioCodec, HttpClie HttpClient = httpClient; } - public String Name { get; private set; } + public String Name { get; private set; } = name; [JsonIgnore] public String? ObjectURL { get; private set; } /// @@ -74,7 +68,7 @@ public Boolean IsContentStreamLoaded [JsonIgnore] public Stream? ContentStream { get; private set; } [JsonIgnore] - public Boolean IsRecorded { get; private set; } + public Boolean IsRecorded { get; private set; } = isRecorded; /// /// Duration of the audio file /// @@ -88,7 +82,7 @@ public AudioCodec? AudioCodec private set { audioCodec = value; - if ((audioCodec != null) && (Name.EndsWith(audioCodec.FileExtension) == false)) + if ((audioCodec != null) && (Name?.EndsWith(audioCodec.FileExtension) == false)) { //Replace file ending Name = String.Format("{0}{1}", Path.GetFileNameWithoutExtension(Name), audioCodec.FileExtension); @@ -107,7 +101,7 @@ public String? AudioFileType audioFileType = AudioCodec.FileExtension.Replace(".", "").ToUpper(); } //Try to find by file name - audioFileType ??= Path.GetExtension(Name).Replace(".", "").ToUpper(); + audioFileType ??= Path.GetExtension(Name)?.Replace(".", "").ToUpper(); return audioFileType; } } diff --git a/AudioCuesheetEditor/Model/IO/Export/ExportfileGenerator.cs b/AudioCuesheetEditor/Model/IO/Export/ExportfileGenerator.cs index a587558e..8190dc1f 100644 --- a/AudioCuesheetEditor/Model/IO/Export/ExportfileGenerator.cs +++ b/AudioCuesheetEditor/Model/IO/Export/ExportfileGenerator.cs @@ -57,7 +57,7 @@ public IReadOnlyCollection GenerateExportfiles() { case ExportType.Cuesheet: content = WriteCuesheet(audioFileName, section); - filename = String.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(ApplicationOptions?.CuesheetFilename), counter, Cuesheet.FileExtension); + filename = String.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(ApplicationOptions?.CuesheetFilename), counter, FileExtensions.Cuesheet); break; case ExportType.Exportprofile: if (Exportprofile != null) diff --git a/AudioCuesheetEditor/Model/IO/FileExtensions.cs b/AudioCuesheetEditor/Model/IO/FileExtensions.cs new file mode 100644 index 00000000..d4b76535 --- /dev/null +++ b/AudioCuesheetEditor/Model/IO/FileExtensions.cs @@ -0,0 +1,24 @@ +//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 FileExtensions + { + public const string Text = ".txt"; + public const string Projectfile = ".ace"; + public const string Cuesheet = ".cue"; + } +} diff --git a/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs b/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs new file mode 100644 index 00000000..a6edaf11 --- /dev/null +++ b/AudioCuesheetEditor/Model/IO/FileMimeTypes.cs @@ -0,0 +1,24 @@ +//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 static class FileMimeTypes + { + public const string Text = "text/plain"; + public const string Projectfile = "text/*"; + public const string Cuesheet = "text/*"; + } +} diff --git a/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs b/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs index 44bd3490..b99d07b0 100644 --- a/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs +++ b/AudioCuesheetEditor/Model/IO/Import/IImportfile.cs @@ -13,6 +13,9 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Services.IO; + namespace AudioCuesheetEditor.Model.IO.Import { public interface IImportfile @@ -20,10 +23,19 @@ public interface IImportfile /// /// File content (each element is a file line) /// - public IEnumerable FileContent { get; set; } + IEnumerable? FileContent { get; set; } /// /// File content with marking which passages has been reconized by scheme /// - public IEnumerable FileContentRecognized { get; } + IEnumerable? FileContentRecognized { get; set; } + /// + /// Exception that has been thrown while readinng out the file + /// + Exception? AnalyseException { get; set; } + /// + /// The cuesheet which was created during analysing the + /// + ImportCuesheet? AnalysedCuesheet { get; set; } + ImportFileType FileType { get; set; } } } diff --git a/AudioCuesheetEditor/Model/IO/Import/Importfile.cs b/AudioCuesheetEditor/Model/IO/Import/Importfile.cs new file mode 100644 index 00000000..033e1989 --- /dev/null +++ b/AudioCuesheetEditor/Model/IO/Import/Importfile.cs @@ -0,0 +1,33 @@ +//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 +//. +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Services.IO; + +namespace AudioCuesheetEditor.Model.IO.Import +{ + public class Importfile : IImportfile + { + /// + public IEnumerable? FileContent { get; set; } + /// + public IEnumerable? FileContentRecognized { get; set; } + /// + public Exception? AnalyseException { get; set; } + /// + public ImportCuesheet? AnalysedCuesheet { get; set; } + public ImportFileType FileType { get; set; } + } +} diff --git a/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs b/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs index 752ba3e1..f5d3e865 100644 --- a/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs +++ b/AudioCuesheetEditor/Model/IO/Import/TextImportScheme.cs @@ -14,6 +14,7 @@ //along with Foobar. If not, see //. using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.Entity; using Blazorise.Localization; @@ -54,6 +55,7 @@ static TextImportScheme() var schemeTrackFlags = String.Format("(?'{0}.{1}'{2})", nameof(Track), nameof(Track.Flags), EnterRegularExpressionHere); var schemeTrackPreGap = String.Format("(?'{0}.{1}'{2})", nameof(Track), nameof(Track.PreGap), EnterRegularExpressionHere); var schemeTrackPostGap = String.Format("(?'{0}.{1}'{2})", nameof(Track), nameof(Track.PostGap), EnterRegularExpressionHere); + var schemeTrackStartDateTime = String.Format("(?'{0}.{1}'{2})", nameof(Track), nameof(ImportTrack.StartDateTime), EnterRegularExpressionHere); AvailableSchemesTrack = new Dictionary { @@ -65,7 +67,8 @@ static TextImportScheme() { nameof(Track.Length), schemeTrackLength }, { nameof(Track.Flags), schemeTrackFlags }, { nameof(Track.PreGap), schemeTrackPreGap }, - { nameof(Track.PostGap), schemeTrackPostGap } + { nameof(Track.PostGap), schemeTrackPostGap }, + { nameof(ImportTrack.StartDateTime), schemeTrackStartDateTime } }; } @@ -122,14 +125,14 @@ protected override ValidationResult Validate(string property) if (SchemeCuesheet?.Contains(availableScheme.Value.Substring(0, availableScheme.Value.IndexOf(EnterRegularExpressionHere))) == true) { var startIndex = SchemeCuesheet.IndexOf(availableScheme.Value.Substring(0, availableScheme.Value.IndexOf(EnterRegularExpressionHere))); - var realRegularExpression = SchemeCuesheet.Substring(startIndex, (SchemeCuesheet.IndexOf(")", startIndex) + 1) - startIndex); - validationMessages ??= new(); + var realRegularExpression = SchemeCuesheet.Substring(startIndex, (SchemeCuesheet.IndexOf(')', startIndex) + 1) - startIndex); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("{0} contains placeholders that can not be solved! Please remove invalid placeholder '{1}'.", nameof(SchemeCuesheet), realRegularExpression)); } } if (SchemeCuesheet?.Contains(enterRegularExpression) == true) { - validationMessages ??= new(); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("Replace '{0}' by a regular expression!", enterRegularExpression)); } break; @@ -140,14 +143,14 @@ protected override ValidationResult Validate(string property) if (SchemeTracks?.Contains(availableScheme.Value.Substring(0, availableScheme.Value.IndexOf(EnterRegularExpressionHere))) == true) { var startIndex = SchemeTracks.IndexOf(availableScheme.Value.Substring(0, availableScheme.Value.IndexOf(EnterRegularExpressionHere))); - var realRegularExpression = SchemeTracks.Substring(startIndex, (SchemeTracks.IndexOf(")", startIndex) + 1) - startIndex); - validationMessages ??= new(); + var realRegularExpression = SchemeTracks.Substring(startIndex, (SchemeTracks.IndexOf(')', startIndex) + 1) - startIndex); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("{0} contains placeholders that can not be solved! Please remove invalid placeholder '{1}'.", nameof(SchemeTracks), realRegularExpression)); } } if (SchemeTracks?.Contains(enterRegularExpression) == true) { - validationMessages ??= new(); + validationMessages ??= []; validationMessages.Add(new ValidationMessage("Replace '{0}' by a regular expression!", enterRegularExpression)); } break; diff --git a/AudioCuesheetEditor/Model/IO/Import/TextImportfile.cs b/AudioCuesheetEditor/Model/IO/Import/TextImportfile.cs deleted file mode 100644 index 3e38db41..00000000 --- a/AudioCuesheetEditor/Model/IO/Import/TextImportfile.cs +++ /dev/null @@ -1,293 +0,0 @@ -//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 -//. -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.IO.Audio; -using AudioCuesheetEditor.Model.Options; -using AudioCuesheetEditor.Model.Utility; -using System.Reflection; -using System.Text.RegularExpressions; - -namespace AudioCuesheetEditor.Model.IO.Import -{ - public class TextImportfile : IImportfile, IDisposable - { - public const String MimeType = "text/plain"; - public const String FileExtension = ".txt"; - - public EventHandler? AnalysisFinished; - - private TextImportScheme textImportScheme; - private TimeSpanFormat? timeSpanFormat; - private bool disposedValue; - private IEnumerable fileContent; - - public TextImportfile(MemoryStream fileContentStream, ImportOptions? importOptions = null) - { - FileContentRecognized = []; - textImportScheme = new TextImportScheme(); - fileContent = []; - fileContentStream.Position = 0; - using var reader = new StreamReader(fileContentStream); - List lines = []; - while (reader.EndOfStream == false) - { - lines.Add(reader.ReadLine()); - } - FileContent = lines.AsReadOnly(); - TimeSpanFormat = new TimeSpanFormat(); - if (importOptions == null) - { - TextImportScheme = TextImportScheme.DefaultTextImportScheme; - } - else - { - if (importOptions.TimeSpanFormat != null) - { - TimeSpanFormat = importOptions.TimeSpanFormat; - } - TextImportScheme = importOptions.TextImportScheme; - } - } - - /// - public IEnumerable FileContent - { - get => fileContent; - set - { - fileContent = value; - Analyse(); - } - } - - /// - public IEnumerable FileContentRecognized { get; private set; } - - public TextImportScheme TextImportScheme - { - get { return textImportScheme; } - set - { - textImportScheme.SchemeChanged -= TextImportScheme_SchemeChanged; - textImportScheme = value; - textImportScheme.SchemeChanged += TextImportScheme_SchemeChanged; - Analyse(); - } - } - - public Exception? AnalyseException { get; private set; } - - public Boolean IsValid { get { return AnalyseException == null; } } - - public Cuesheet? Cuesheet { get; private set; } - - public TimeSpanFormat TimeSpanFormat - { - get - { - timeSpanFormat ??= new TimeSpanFormat(); - return timeSpanFormat; - } - set - { - if (timeSpanFormat != null) - { - timeSpanFormat.SchemeChanged -= TimeSpanFormat_SchemeChanged; - } - timeSpanFormat = value; - if (timeSpanFormat != null) - { - timeSpanFormat.SchemeChanged += TimeSpanFormat_SchemeChanged; - } - } - } - - private void TextImportScheme_SchemeChanged(object? sender, string e) - { - Analyse(); - } - - private void TimeSpanFormat_SchemeChanged(object? sender, EventArgs eventArgs) - { - Analyse(); - } - - private void Analyse() - { - try - { - Cuesheet = new Cuesheet(); - FileContentRecognized = []; - AnalyseException = null; - Boolean cuesheetRecognized = false; - List recognizedFileContent = []; - foreach (var line in FileContent) - { - var recognizedLine = line; - if (String.IsNullOrEmpty(line) == false) - { - Boolean recognized = false; - if ((recognized == false) && (cuesheetRecognized == false) && (String.IsNullOrEmpty(TextImportScheme.SchemeCuesheet) == false)) - { - //Remove entity names - var expression = TextImportScheme.SchemeCuesheet.Replace(String.Format("{0}.", nameof(AudioCuesheet.Cuesheet)), String.Empty).Replace(String.Format("{0}.", nameof(Track)), String.Empty); - var regExCuesheet = new Regex(expression); - recognizedLine = AnalyseLine(line, Cuesheet, regExCuesheet); - recognized = recognizedLine != null; - cuesheetRecognized = recognizedLine != null; - } - if ((recognized == false) && (String.IsNullOrEmpty(TextImportScheme.SchemeTracks) == false)) - { - //Remove entity names - var expression = TextImportScheme.SchemeTracks.Replace(String.Format("{0}.", nameof(AudioCuesheet.Cuesheet)), String.Empty).Replace(String.Format("{0}.", nameof(Track)), String.Empty); - var regExTracks = new Regex(expression); - var track = new Track(); - recognizedLine = AnalyseLine(line, track, regExTracks); - recognized = recognizedLine != null; - Cuesheet.AddTrack(track); - } - } - recognizedFileContent.Add(recognizedLine); - } - if (recognizedFileContent.Count > 0) - { - FileContentRecognized = recognizedFileContent.AsReadOnly(); - } - } - catch (Exception ex) - { - FileContentRecognized = FileContent; - AnalyseException = ex; - Cuesheet = null; - } - AnalysisFinished?.Invoke(this, EventArgs.Empty); - } - - /// - /// Analyses a line and sets the properties on the entity - /// - /// Line to analyse - /// Entity to set properties on - /// Regular expression to use for analysis - /// Analysed line with marking what has been matched or empty string - /// Occurs when property or group could not be found! - private String? AnalyseLine(String line, ICuesheetEntity entity, Regex regex) - { - String? recognized = null; - string? recognizedLine = line; - if (String.IsNullOrEmpty(line) == false) - { - var match = regex.Match(line); - if (match.Success) - { - for (int groupCounter = 1; groupCounter < match.Groups.Count; groupCounter++) - { - var key = match.Groups.Keys.ElementAt(groupCounter); - var group = match.Groups.GetValueOrDefault(key); - if ((group != null) && (group.Success)) - { - var property = entity.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - if (property != null) - { - SetValue(entity, property, group.Value); - recognizedLine = string.Concat(recognizedLine.AsSpan(0, group.Index + (13 * (groupCounter - 1))) - , String.Format(CuesheetConstants.RecognizedMarkHTML, group.Value) - , recognizedLine.AsSpan(group.Index + (13 * (groupCounter - 1)) + group.Length)); - } - else - { - throw new NullReferenceException(String.Format("Property '{0}' was not found for line content {1}", key, line)); - } - } - else - { - throw new NullReferenceException(String.Format("Group '{0}' could not be found!", key)); - } - } - if (recognizedLine.Contains(CuesheetConstants.RecognizedMarkHTML.Substring(0, CuesheetConstants.RecognizedMarkHTML.IndexOf("{0}")))) - { - recognized = recognizedLine; - } - } - else - { - recognized = line; - } - } - return recognized; - } - - /// - /// Set the value on the entity - /// - /// Entity object to set the value on - /// Property to set - /// Value to set - private void SetValue(ICuesheetEntity entity, PropertyInfo property, string value) - { - if (property.PropertyType == typeof(TimeSpan?)) - { - var utility = new DateTimeUtility(TimeSpanFormat); - property.SetValue(entity, utility.ParseTimeSpan(value)); - } - if (property.PropertyType == typeof(uint?)) - { - property.SetValue(entity, Convert.ToUInt32(value)); - } - if (property.PropertyType == typeof(String)) - { - property.SetValue(entity, value); - } - if (property.PropertyType == typeof(IReadOnlyCollection)) - { - var list = Flag.AvailableFlags.Where(x => value.Contains(x.CuesheetLabel)); - ((Track)entity).SetFlags(list); - } - if (property.PropertyType == typeof(Audiofile)) - { - property.SetValue(entity, new Audiofile(value)); - } - if (property.PropertyType == typeof(Cataloguenumber)) - { - ((Cuesheet)entity).Cataloguenumber.Value = value; - } - if (property.PropertyType == typeof(CDTextfile)) - { - property.SetValue(entity, new CDTextfile(value)); - } - } - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - TimeSpanFormat.SchemeChanged -= TimeSpanFormat_SchemeChanged; - textImportScheme.SchemeChanged -= TextImportScheme_SchemeChanged; - } - disposedValue = true; - } - } - - public void Dispose() - { - // Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "Dispose(bool disposing)" ein. - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/AudioCuesheetEditor/Model/IO/Projectfile.cs b/AudioCuesheetEditor/Model/IO/Projectfile.cs index f7b4b2ec..da0b73e2 100644 --- a/AudioCuesheetEditor/Model/IO/Projectfile.cs +++ b/AudioCuesheetEditor/Model/IO/Projectfile.cs @@ -20,11 +20,8 @@ namespace AudioCuesheetEditor.Model.IO { - public class Projectfile + public class Projectfile(Cuesheet cuesheet) { - public const String MimeType = "text/*"; - public const String FileExtension = ".ace"; - public static readonly String DefaultFilename = "Project.ace"; public static readonly JsonSerializerOptions Options = new() @@ -39,16 +36,7 @@ public class Projectfile return JsonSerializer.Deserialize(json, Options); } - public Projectfile(Cuesheet cuesheet) - { - if (cuesheet == null) - { - throw new ArgumentNullException(nameof(cuesheet)); - } - Cuesheet = cuesheet; - } - - public Cuesheet Cuesheet { get; private set; } + public Cuesheet Cuesheet { get; private set; } = cuesheet; /// /// Generate a ProjectFile diff --git a/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs b/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs index 2f33d15d..79f88022 100644 --- a/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs +++ b/AudioCuesheetEditor/Model/Options/ApplicationOptions.cs @@ -13,7 +13,6 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Entity; using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Export; @@ -110,10 +109,10 @@ protected override ValidationResult Validate(string property) else { var extension = Path.GetExtension(CuesheetFilename); - if (extension.Equals(Cuesheet.FileExtension, StringComparison.OrdinalIgnoreCase) == false) + if (extension.Equals(FileExtensions.Cuesheet, StringComparison.OrdinalIgnoreCase) == false) { validationMessages ??= []; - validationMessages.Add(new ValidationMessage("{0} must end with '{1}'!", nameof(CuesheetFilename), Cuesheet.FileExtension)); + validationMessages.Add(new ValidationMessage("{0} must end with '{1}'!", nameof(CuesheetFilename), FileExtensions.Cuesheet)); } var filenameWithoutExtension = Path.GetFileNameWithoutExtension(CuesheetFilename); if (string.IsNullOrEmpty(filenameWithoutExtension)) @@ -133,10 +132,10 @@ protected override ValidationResult Validate(string property) else { var extension = Path.GetExtension(ProjectFilename); - if (extension.Equals(Projectfile.FileExtension, StringComparison.OrdinalIgnoreCase) == false) + if (extension.Equals(FileExtensions.Projectfile, StringComparison.OrdinalIgnoreCase) == false) { validationMessages ??= []; - validationMessages.Add(new ValidationMessage("{0} must end with '{1}'!", nameof(ProjectFilename), Projectfile.FileExtension)); + validationMessages.Add(new ValidationMessage("{0} must end with '{1}'!", nameof(ProjectFilename), FileExtensions.Projectfile)); } var filename = Path.GetFileNameWithoutExtension(ProjectFilename); if (String.IsNullOrEmpty(filename)) diff --git a/AudioCuesheetEditor/Model/Options/ImportOptions.cs b/AudioCuesheetEditor/Model/Options/ImportOptions.cs index 1ee000a1..5131d11e 100644 --- a/AudioCuesheetEditor/Model/Options/ImportOptions.cs +++ b/AudioCuesheetEditor/Model/Options/ImportOptions.cs @@ -21,18 +21,7 @@ namespace AudioCuesheetEditor.Model.Options { public class ImportOptions : IOptions { - public TextImportScheme TextImportScheme { get; set; } - public TimeSpanFormat? TimeSpanFormat { get; set; } - - public ImportOptions() - { - TextImportScheme = TextImportScheme.DefaultTextImportScheme; - } - - public ImportOptions(TextImportfile textImportfile) - { - TextImportScheme = textImportfile.TextImportScheme; - TimeSpanFormat = textImportfile.TimeSpanFormat; - } + public TextImportScheme TextImportScheme { get; set; } = TextImportScheme.DefaultTextImportScheme; + public TimeSpanFormat TimeSpanFormat { get; set; } = new(); } } diff --git a/AudioCuesheetEditor/Model/UI/TraceChangeManager.cs b/AudioCuesheetEditor/Model/UI/TraceChangeManager.cs index 78b5ca1c..a4321ad1 100644 --- a/AudioCuesheetEditor/Model/UI/TraceChangeManager.cs +++ b/AudioCuesheetEditor/Model/UI/TraceChangeManager.cs @@ -13,28 +13,15 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Pages; -using Markdig.Extensions.Yaml; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - namespace AudioCuesheetEditor.Model.UI { /// /// Class for tracing changes on an object /// - public class TracedChange + public class TracedChange(ITraceable traceableObject, TraceableChange traceableChange) { - private readonly WeakReference _tracedObject; - public TracedChange(ITraceable traceableObject, TraceableChange traceableChange) - { - _tracedObject = new WeakReference(traceableObject, false); - TraceableChange = traceableChange; - } + private readonly WeakReference _tracedObject = new(traceableObject, false); + public ITraceable? TraceableObject { get @@ -47,26 +34,21 @@ public ITraceable? TraceableObject } } - public TraceableChange TraceableChange { get; } + public TraceableChange TraceableChange { get; } = traceableChange; } - public class TracedChanges + public class TracedChanges(IEnumerable changes) { - public TracedChanges(IEnumerable changes) - { - Changes = new(changes); - } - - public List Changes { get; } + public List Changes { get; } = new(changes); public Boolean HasTraceableObject { get { return Changes.Any(x => x.TraceableObject != null); } } } /// /// Manager for Undo and Redo operations on objects. /// - public class TraceChangeManager + public class TraceChangeManager(ILogger logger) { - private readonly ILogger _logger; + private readonly ILogger _logger = logger; private readonly Stack undoStack = new(); private readonly Stack redoStack = new(); @@ -77,7 +59,7 @@ public class TraceChangeManager public event EventHandler? UndoDone; public event EventHandler? RedoDone; - public Boolean CurrentlyHandlingRedoOrUndoChanges { get; private set; } + public Boolean CurrentlyHandlingRedoOrUndoChanges { get; private set; } = false; /// /// Is Undo() currently possible (are there any changes)? /// @@ -100,12 +82,6 @@ public Boolean CanRedo } } - public TraceChangeManager(ILogger logger) - { - CurrentlyHandlingRedoOrUndoChanges = false; - _logger = logger; - } - public void TraceChanges(ITraceable traceable) { traceable.TraceablePropertyChanged += Traceable_TraceablePropertyChanged; @@ -229,7 +205,7 @@ public Boolean BulkEdit _logger.LogDebug("Set BulkEdit called with {value}", value); if (value) { - bulkEditTracedChanges = new(); + bulkEditTracedChanges = []; } else { @@ -293,7 +269,7 @@ private void Traceable_TraceablePropertyChanged(object? sender, TraceablePropert if (BulkEdit == false) { //Single change - var changes = new TracedChanges(new List() { new((ITraceable)sender, e.TraceableChange) }); + var changes = new TracedChanges([new((ITraceable)sender, e.TraceableChange)]); undoStack.Push(changes); redoStack.Clear(); TracedObjectHistoryChanged?.Invoke(this, EventArgs.Empty); diff --git a/AudioCuesheetEditor/Model/Utility/IOUtility.cs b/AudioCuesheetEditor/Model/Utility/IOUtility.cs index 450d14de..eade907c 100644 --- a/AudioCuesheetEditor/Model/Utility/IOUtility.cs +++ b/AudioCuesheetEditor/Model/Utility/IOUtility.cs @@ -27,13 +27,13 @@ public static Boolean CheckFileMimeType(IFileEntry file, String mimeType, String { if (String.IsNullOrEmpty(file.Type) == false) { - fileMimeTypeMatches = file.Type.ToLower() == mimeType.ToLower(); + fileMimeTypeMatches = file.Type.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase); } else { //Try to find by file extension - var extension = Path.GetExtension(file.Name).ToLower(); - fileMimeTypeMatches = extension == fileExtension.ToLower(); + var extension = Path.GetExtension(file.Name); + fileMimeTypeMatches = extension.Equals(fileExtension, StringComparison.CurrentCultureIgnoreCase); } } return fileMimeTypeMatches; @@ -47,7 +47,7 @@ public static Boolean CheckFileMimeTypeForAudioCodec(IFileEntry file) public static AudioCodec? GetAudioCodec(IFileEntry fileEntry) { AudioCodec? foundAudioCodec = null; - var extension = Path.GetExtension(fileEntry.Name).ToLower(); + var extension = Path.GetExtension(fileEntry.Name); // First search with mime type and file extension var audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(fileEntry.Type, StringComparison.OrdinalIgnoreCase) && x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase)); if (audioCodecsFound.Count() <= 1) diff --git a/AudioCuesheetEditor/Model/Utility/TimeSpanUtility.cs b/AudioCuesheetEditor/Model/Utility/TimeSpanUtility.cs new file mode 100644 index 00000000..7ca259e8 --- /dev/null +++ b/AudioCuesheetEditor/Model/Utility/TimeSpanUtility.cs @@ -0,0 +1,40 @@ +//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.Utility +{ + public class TimeSpanUtility + { + public static TimeSpan? ParseTimeSpan(String input, TimeSpanFormat? timeSpanFormat = null) + { + TimeSpan? result = null; + if (String.IsNullOrEmpty(input) == false) + { + if (String.IsNullOrEmpty(timeSpanFormat?.Scheme)) + { + if (TimeSpan.TryParse(input, out var parsed)) + { + result = parsed; + } + } + else + { + result = timeSpanFormat.ParseTimeSpan(input); + } + } + return result; + } + } +} diff --git a/AudioCuesheetEditor/Pages/EditSections.razor b/AudioCuesheetEditor/Pages/EditSections.razor index 4f9c19ea..ebeb8d8a 100644 --- a/AudioCuesheetEditor/Pages/EditSections.razor +++ b/AudioCuesheetEditor/Pages/EditSections.razor @@ -20,7 +20,7 @@ along with Foobar. If not, see @inject ITextLocalizer _localizer @inject SessionStateContainer _sessionStateContainer @inject ITextLocalizer _validationMessageLocalizer -@inject DateTimeUtility _dateTimeUtility +@inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser @inject ITextLocalizerService _localizationService @inject TraceChangeManager _traceChangeManager @inject IJSRuntime _jsRuntime @@ -77,7 +77,7 @@ along with Foobar. If not, see - + @@ -86,7 +86,7 @@ along with Foobar. If not, see - + diff --git a/AudioCuesheetEditor/Pages/ImportFileView.razor b/AudioCuesheetEditor/Pages/ImportFileView.razor index f88c801c..f586cccc 100644 --- a/AudioCuesheetEditor/Pages/ImportFileView.razor +++ b/AudioCuesheetEditor/Pages/ImportFileView.razor @@ -78,7 +78,7 @@ along with Foobar. If not, see if (newTabName == "editFilecontent") { //Set fileContent just when component is visible in order to autosize the MemoEdit - if (_sessionStateContainer.Importfile != null) + if (_sessionStateContainer.Importfile?.FileContent != null) { fileContent = String.Join(Environment.NewLine, _sessionStateContainer.Importfile.FileContent); } diff --git a/AudioCuesheetEditor/Pages/Index.razor b/AudioCuesheetEditor/Pages/Index.razor index 7eb0dbd9..b9d76fc5 100644 --- a/AudioCuesheetEditor/Pages/Index.razor +++ b/AudioCuesheetEditor/Pages/Index.razor @@ -15,14 +15,13 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> - -@implements IDisposable +@implements IAsyncDisposable @page "/" @inject IJSRuntime _jsRuntime @inject ITextLocalizer _localizer @inject NavigationManager _navigationManager -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject ILogger _logger @inject HotKeys _hotKeys @inject HttpClient _httpClient @@ -78,12 +77,15 @@ along with Foobar. If not, see @code { - - public void Dispose() + + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; _sessionStateContainer.CurrentViewModeChanged -= CurrentViewModeChanged; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } [CascadingParameter] diff --git a/AudioCuesheetEditor/Pages/RecordControl.razor b/AudioCuesheetEditor/Pages/RecordControl.razor index 3fded530..a167e0fd 100644 --- a/AudioCuesheetEditor/Pages/RecordControl.razor +++ b/AudioCuesheetEditor/Pages/RecordControl.razor @@ -15,12 +15,12 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> -@implements IDisposable +@implements IAsyncDisposable @inject SessionStateContainer _sessionStateContainer @inject ITextLocalizer _localizer @inject ITextLocalizerService _localizationService -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject HotKeys _hotKeys @if (_sessionStateContainer.Cuesheet.IsRecording == true) @@ -131,12 +131,15 @@ along with Foobar. If not, see [Parameter] public EventCallback StopRecordClicked { get; set; } - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionsSaved; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } protected override async Task OnInitializedAsync() diff --git a/AudioCuesheetEditor/Pages/ViewModeImport.razor b/AudioCuesheetEditor/Pages/ViewModeImport.razor index 758628e8..03cc51b3 100644 --- a/AudioCuesheetEditor/Pages/ViewModeImport.razor +++ b/AudioCuesheetEditor/Pages/ViewModeImport.razor @@ -20,13 +20,12 @@ along with Foobar. If not, see @inject ITextLocalizer _localizer @inject SessionStateContainer _sessionStateContainer -@inject LocalStorageOptionsProvider _localStorageOptionsProvider -@inject ILogger _logger +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject ITextLocalizerService _localizationService @inject HotKeys _hotKeys @inject IJSRuntime _jsRuntime @inject HttpClient _httpClient -@inject ITextLocalizer _validationMessageLocalizer +@inject ImportManager _importManager @@ -57,7 +56,7 @@ along with Foobar. If not, see @_localizer["Textfile"] - + @@ -73,7 +72,7 @@ along with Foobar. If not, see @_localizer["Cuesheet"] - + @@ -89,7 +88,7 @@ along with Foobar. If not, see @_localizer["Project filename"] - + @@ -119,132 +118,36 @@ along with Foobar. If not, see - - - - - - - - - @if (_sessionStateContainer.TextImportFile != null) + @if (displayFileContent) + { + + + + + + + + + } + @if (displayEditImportOptions) { - - - - - @_localizer["Textimportscheme cuesheet"] - - - - - - - - - - - - - - - @_localizer["Select placeholder"] - - - @foreach (var availableSchemeTrack in TextImportScheme.AvailableSchemeCuesheet) - { - @_localizer[availableSchemeTrack.Key] - } - - - - - - - - - - - - @_localizer["Textimportscheme track"] - - - - - - - - - - - - - - - @_localizer["Select placeholder"] - - - @foreach (var availableSchemeTrack in TextImportScheme.AvailableSchemesTrack) - { - @_localizer[availableSchemeTrack.Key] - } - - - - - - - - - - - - @_localizer["Customized timespan format import"] - - - - - - - - - - - - - - - @_localizer["Select placeholder"] - - - @foreach (var availableFormat in TimeSpanFormat.AvailableTimespanScheme) - { - @_localizer[availableFormat.Key] - } - - - - - - - + @_localizer["Reset import options"] - - @if (_sessionStateContainer.TextImportFile.AnalyseException != null) + @if (_sessionStateContainer.Importfile?.AnalyseException != null) { @@ -253,7 +156,7 @@ along with Foobar. If not, see - @_localizer["Error during textimport"] : @_sessionStateContainer.TextImportFile.AnalyseException.Message + @_localizer["Error during textimport"] : @_sessionStateContainer.Importfile.AnalyseException.Message } } @@ -296,7 +199,7 @@ along with Foobar. If not, see @code { String selectedStep = "selectFiles"; - String dragNDropUploadFilter = String.Join(',', TextImportfile.MimeType, Cuesheet.FileExtension, Projectfile.FileExtension); + String dragNDropUploadFilter = String.Join(',', FileMimeTypes.Text, FileExtensions.Cuesheet, FileExtensions.Projectfile); Boolean cuesheetDataVisible = true; Boolean cuesheetTracksVisible = true; Boolean cuesheetSplitPointsVisible = true; @@ -305,6 +208,8 @@ along with Foobar. If not, see Boolean selectFilesCompleted = false; Boolean userChangedSelectedStep = false; Boolean displaySplitPoints = false; + Boolean displayFileContent = true; + Boolean displayEditImportOptions = true; Alert? alertInvalidFile; ModalDialog? modalDialog; List invalidTextImportFileNames = new(); @@ -317,25 +222,20 @@ along with Foobar. If not, see public void Dispose() { - hotKeysContext?.Dispose(); + hotKeysContext?.DisposeAsync(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; } - protected override Task OnInitializedAsync() + protected override void OnInitialized() { - _logger.LogDebug("OnInitializedAsync"); + base.OnInitialized(); _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; hotKeysContext = _hotKeys.CreateContext() .Add(Key.Enter, OnEnterKeyDown); - - TimeSpanFormat.TextLocalizer = _localizer; - TextImportScheme.TextLocalizer = _localizer; - - return Task.CompletedTask; } async Task OnSelectedStepChanged(String name) @@ -347,7 +247,7 @@ along with Foobar. If not, see selectedStep = name; break; case "validateData": - if ((_sessionStateContainer.ImportCuesheet != null) || (_sessionStateContainer.TextImportFile != null)) + if ((_sessionStateContainer.ImportCuesheet != null) || (_sessionStateContainer.Importfile != null)) { selectFilesCompleted = true; selectedStep = name; @@ -371,7 +271,7 @@ along with Foobar. If not, see { get { - if ((userChangedSelectedStep == false) && ((_sessionStateContainer.ImportCuesheet != null) || (_sessionStateContainer.TextImportFile != null))) + if ((userChangedSelectedStep == false) && ((_sessionStateContainer.ImportCuesheet != null) || (_sessionStateContainer.Importfile != null))) { selectFilesCompleted = true; selectedStep = "validateData"; @@ -386,7 +286,7 @@ along with Foobar. If not, see if (e.Files.FirstOrDefault() != null) { var file = e.Files.First(); - if (IOUtility.CheckFileMimeType(file, TextImportfile.MimeType, TextImportfile.FileExtension) == false) + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Text, FileExtensions.Text) == false) { invalidTextImportFileNames.Add(file.Name); } @@ -404,7 +304,7 @@ along with Foobar. If not, see if (e.Files.FirstOrDefault() != null) { var file = e.Files.First(); - if (IOUtility.CheckFileMimeType(file, Cuesheet.MimeType, Cuesheet.FileExtension) == false) + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Cuesheet, FileExtensions.Cuesheet) == false) { invalidCuesheetfileNames.Add(file.Name); } @@ -422,7 +322,7 @@ along with Foobar. If not, see if (e.Files.FirstOrDefault() != null) { var file = e.Files.First(); - if (IOUtility.CheckFileMimeType(file, Projectfile.MimeType, Projectfile.FileExtension) == false) + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Projectfile, FileExtensions.Projectfile) == false) { invalidProjectfileNames.Add(file.Name); } @@ -439,9 +339,9 @@ along with Foobar. If not, see invalidDropFileNames.Clear(); foreach (var file in e.Files) { - if ((IOUtility.CheckFileMimeType(file, Projectfile.MimeType, Projectfile.FileExtension) == false) - && (IOUtility.CheckFileMimeType(file, Cuesheet.MimeType, Cuesheet.FileExtension) == false) - && (IOUtility.CheckFileMimeType(file, TextImportfile.MimeType, TextImportfile.FileExtension) == false) + if ((IOUtility.CheckFileMimeType(file, FileMimeTypes.Projectfile, FileExtensions.Projectfile) == false) + && (IOUtility.CheckFileMimeType(file, FileMimeTypes.Cuesheet, FileExtensions.Cuesheet) == false) + && (IOUtility.CheckFileMimeType(file, FileMimeTypes.Text, FileExtensions.Text) == false) && (IOUtility.CheckFileMimeTypeForAudioCodec(file) == false)) { invalidDropFileNames.Add(file.Name); @@ -457,57 +357,23 @@ along with Foobar. If not, see private async Task OnFileChanged(IReadOnlyCollection files) { _sessionStateContainer.ResetImport(); - displaySplitPoints = false; + var importedFiles = await _importManager.ImportFilesAsync(files); + // Audio file is handled seperatly foreach (var file in files) { - if (IOUtility.CheckFileMimeType(file, Projectfile.MimeType, Projectfile.FileExtension)) - { - //We have a valid file here - var fileContent = new MemoryStream(); - var stream = file.OpenReadStream(); - await stream.CopyToAsync(fileContent); - stream.Close(); - var cuesheet = Projectfile.ImportFile(fileContent.ToArray()); - if (cuesheet != null) - { - _sessionStateContainer.ImportCuesheet = cuesheet; - } - displaySplitPoints = true; - } - if (IOUtility.CheckFileMimeType(file, Cuesheet.MimeType, Cuesheet.FileExtension) == true) - { - var options = await _localStorageOptionsProvider.GetOptions(); - var stream = file.OpenReadStream(); - MemoryStream memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream); - stream.Close(); - _sessionStateContainer.CuesheetImportFile = new CuesheetImportfile(memoryStream, options); - } - if (IOUtility.CheckFileMimeType(file, TextImportfile.MimeType, TextImportfile.FileExtension) == true) - { - var options = await _localStorageOptionsProvider.GetOptions(); - var stream = file.OpenReadStream(); - MemoryStream memoryStream = new MemoryStream(); - await stream.CopyToAsync(memoryStream); - stream.Close(); - if (_sessionStateContainer.TextImportFile != null) - { - _sessionStateContainer.TextImportFile.TimeSpanFormat.ValidateablePropertyChanged -= Timespanformat_ValidateablePropertyChanged; - _sessionStateContainer.TextImportFile.TextImportScheme.ValidateablePropertyChanged -= TextImportScheme_ValidateablePropertyChanged; - } - _sessionStateContainer.TextImportFile = new TextImportfile(memoryStream, options); - _sessionStateContainer.TextImportFile.TimeSpanFormat.ValidateablePropertyChanged += Timespanformat_ValidateablePropertyChanged; - _sessionStateContainer.TextImportFile.TextImportScheme.ValidateablePropertyChanged += TextImportScheme_ValidateablePropertyChanged; - } - if (IOUtility.CheckFileMimeTypeForAudioCodec(file) == true) + if (IOUtility.CheckFileMimeTypeForAudioCodec(file)) { var audioFileObjectURL = await _jsRuntime.InvokeAsync("getObjectURL", "dropFileInput"); var codec = IOUtility.GetAudioCodec(file); var audiofile = new Audiofile(file.Name, audioFileObjectURL, codec, _httpClient); _ = audiofile.LoadContentStream(); _sessionStateContainer.ImportAudiofile = audiofile; + importedFiles.Add(file, ImportFileType.Audiofile); } } + displaySplitPoints = importedFiles.ContainsValue(ImportFileType.ProjectFile); + displayFileContent = !importedFiles.ContainsValue(ImportFileType.ProjectFile); + displayEditImportOptions = importedFiles.ContainsValue(ImportFileType.Textfile); await OnSelectedStepChanged("validateData"); StateHasChanged(); } @@ -534,14 +400,7 @@ along with Foobar. If not, see private async Task ImportData() { - var options = await _localStorageOptionsProvider.GetOptions(); - if (_sessionStateContainer.TextImportFile != null) - { - //Save import options (values are inside the TextImportFile) - var importOptions = new ImportOptions(_sessionStateContainer.TextImportFile); - await _localStorageOptionsProvider.SaveOptions(importOptions); - } - _sessionStateContainer.StartImportCuesheet(options); + await _importManager.ImportCuesheetAsync(); _sessionStateContainer.CurrentViewMode = ViewMode.ViewModeFull; StateHasChanged(); } @@ -555,8 +414,6 @@ along with Foobar. If not, see private void LocalizationService_LocalizationChanged(object? sender, EventArgs args) { StateHasChanged(); - TimeSpanFormat.TextLocalizer = _localizer; - TextImportScheme.TextLocalizer = _localizer; validations?.ValidateAll(); } @@ -591,36 +448,17 @@ along with Foobar. If not, see { var newOptions = new ImportOptions(); await _localStorageOptionsProvider.SaveOptions(newOptions); - await OnReloadImportOptionsClicked(); } - async Task OnReloadImportOptionsClicked() + void TextImportScheme_ValidateablePropertyChanged(object? sender, String property) { - var options = await _localStorageOptionsProvider.GetOptions(); - if (_sessionStateContainer.TextImportFile != null) + if (validations != null) { - _sessionStateContainer.TextImportFile.TimeSpanFormat.ValidateablePropertyChanged -= Timespanformat_ValidateablePropertyChanged; - _sessionStateContainer.TextImportFile.TextImportScheme.ValidateablePropertyChanged -= TextImportScheme_ValidateablePropertyChanged; - if (options.TimeSpanFormat != null) - { - _sessionStateContainer.TextImportFile.TimeSpanFormat = options.TimeSpanFormat; - } - else - { - _sessionStateContainer.TextImportFile.TimeSpanFormat = new TimeSpanFormat(); - } - _sessionStateContainer.TextImportFile.TimeSpanFormat.ValidateablePropertyChanged += Timespanformat_ValidateablePropertyChanged; - _sessionStateContainer.TextImportFile.TextImportScheme = options.TextImportScheme; - _sessionStateContainer.TextImportFile.TextImportScheme.ValidateablePropertyChanged += TextImportScheme_ValidateablePropertyChanged; - if (validations != null) - { - validations.ValidateAll().GetAwaiter().GetResult(); - } - StateHasChanged(); + validations.ValidateAll().GetAwaiter().GetResult(); } } - void TextImportScheme_ValidateablePropertyChanged(object? sender, String property) + void Timespanformat_ValidateablePropertyChanged(object? sender, String property) { if (validations != null) { @@ -628,11 +466,11 @@ along with Foobar. If not, see } } - void Timespanformat_ValidateablePropertyChanged(object? sender, String property) + async Task EditImportOptions_OptionsChanged(ImportOptions importOptions) { - if (validations != null) + if ((_sessionStateContainer.Importfile?.FileType == ImportFileType.Textfile) && (_sessionStateContainer.Importfile?.FileContent != null)) { - validations.ValidateAll().GetAwaiter().GetResult(); + await _importManager.ImportTextAsync(_sessionStateContainer.Importfile.FileContent); } } } diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs index 287ee61f..ca84b218 100644 --- a/AudioCuesheetEditor/Program.cs +++ b/AudioCuesheetEditor/Program.cs @@ -19,6 +19,8 @@ using AudioCuesheetEditor.Extensions; using AudioCuesheetEditor.Model.UI; using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditor.Services.IO; +using AudioCuesheetEditor.Services.UI; using BlazorDownloadFile; using Blazorise; using Blazorise.Bootstrap5; @@ -47,12 +49,15 @@ builder.Services.AddBlazorDownloadFile(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddLogging(); builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging")); diff --git a/AudioCuesheetEditor/Resources/Localization/EditImportOptions/de.json b/AudioCuesheetEditor/Resources/Localization/EditImportOptions/de.json new file mode 100644 index 00000000..1d41f3e2 --- /dev/null +++ b/AudioCuesheetEditor/Resources/Localization/EditImportOptions/de.json @@ -0,0 +1,33 @@ +{ + "culture": "de", + "translations": { + "Enter textimportscheme cuesheet tooltip": "Textschema für den Import von Cuesheeteigenschaften hier eingeben. Die Identifikation wird über reguläre Ausdrücke ausgeführt.", + "Enter textimportscheme cuesheet here": "Textschema für den Import von Cuesheeteigenschaften hier eingeben", + "Select placeholder": "Platzhalter auswählen", + "Enter textimportscheme track tooltip": "Textschema für den Import von Titeleigenschaften hier eingeben. Die Identifikation wird über reguläre Ausdrücke ausgeführt.", + "Enter textimportscheme track here": "Textschema für den Import von Titeleigenschaften hier eingeben", + "Textimportscheme cuesheet": "Import Schema Cuesheet", + "Textimportscheme track": "Import Schema Titel", + "Import Options": "Importoptionen", + "ENTER REGULAR EXPRESSION HERE": "Hier regulären Ausdruck eingeben", + "Enter custom timespan format here": "Hier können Sie bei Bedarf ein angepasstes Format für Zeitspannen eingeben. Details können der Hilfe entnommen werden.", + "Customized timespan format import": "Zeitspannenformat für Import", + "CD artist": "CD Künstler", + "CD title": "CD Titel", + "Audiofile": "Audiodatei", + "Change": "Ändern", + "Cataloguenumber": "Katalognummer", + "CD textfile": "CD Textdatei", + "Artist": "Künstler", + "Title": "Titel", + "Begin": "Beginn", + "End": "Ende", + "Length": "Länge", + "Days": "Tage", + "Hours": "Stunden", + "Minutes": "Minuten", + "Seconds": "Sekunden", + "Milliseconds": "Millisekunden", + "StartDateTime": "Startzeitpunkt" + } +} \ No newline at end of file diff --git a/AudioCuesheetEditor/Resources/Localization/EditImportOptions/en.json b/AudioCuesheetEditor/Resources/Localization/EditImportOptions/en.json new file mode 100644 index 00000000..b453cd98 --- /dev/null +++ b/AudioCuesheetEditor/Resources/Localization/EditImportOptions/en.json @@ -0,0 +1,32 @@ +{ + "culture": "en", + "translations": { + "Enter textimportscheme cuesheet tooltip": "Enter textscheme for cuesheet properties import here. Identification will be done using regular expressions.", + "Enter textimportscheme cuesheet here": "Enter textscheme for cuesheet properties import here", + "Select placeholder": "Select placeholder", + "Enter textimportscheme track tooltip": "Enter textscheme for track properties import here. Identification will be done using regular expressions.", + "Enter textimportscheme track here": "Enter textscheme for track properties import here", + "Textimportscheme cuesheet": "Textimport scheme cuesheet", + "Textimportscheme track": "Textimport scheme track", + "Import Options": "Import options", + "ENTER REGULAR EXPRESSION HERE": "Enter regular expression here", + "Enter custom timespan format here": "You can enter a custom format for timespan here. Details can be found in help.", + "Customized timespan format import": "Timespan format for import", + "CD artist": "CD artist", + "CD title": "CD title", + "Audiofile": "Audiofile", + "CD textfile": "CD Textfile", + "Cataloguenumber": "Cataloguenumber", + "Artist": "Artist", + "Title": "Title", + "Begin": "Begin", + "End": "End", + "Length": "Length", + "Days": "Days", + "Hours": "Hours", + "Minutes": "Minutes", + "Seconds": "Seconds", + "Milliseconds": "Milliseconds", + "StartDateTime": "Startdatetime" + } +} \ No newline at end of file diff --git a/AudioCuesheetEditor/Model/IO/Import/CuesheetImportfile.cs b/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs similarity index 87% rename from AudioCuesheetEditor/Model/IO/Import/CuesheetImportfile.cs rename to AudioCuesheetEditor/Services/IO/CuesheetImportService.cs index c5656c54..648109dc 100644 --- a/AudioCuesheetEditor/Model/IO/Import/CuesheetImportfile.cs +++ b/AudioCuesheetEditor/Services/IO/CuesheetImportService.cs @@ -1,4 +1,4 @@ -//This file is part of AudioCuesheetEditor. +//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 @@ -14,55 +14,23 @@ //along with Foobar. If not, see //. using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.IO.Audio; -using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Model.IO.Import; using System.Text.RegularExpressions; -namespace AudioCuesheetEditor.Model.IO.Import +namespace AudioCuesheetEditor.Services.IO { - public class CuesheetImportfile : IImportfile + public class CuesheetImportService { - private IEnumerable fileContent; - - public EventHandler? AnalysisFinished; - - /// - public IEnumerable FileContent - { - get => fileContent; - set - { - fileContent = value; - Analyse(); - } - } - - /// - public IEnumerable FileContentRecognized { get; private set; } - public Exception? AnalyseException { get; private set; } - public Cuesheet? Cuesheet { get; private set; } - public ApplicationOptions ApplicationOptions { get; private set; } - - public CuesheetImportfile(MemoryStream fileContentStream, ApplicationOptions applicationOptions) + public static IImportfile Analyse(IEnumerable fileContent) { - FileContentRecognized = []; - fileContentStream.Position = 0; - using var reader = new StreamReader(fileContentStream); - List lines = []; - while (reader.EndOfStream == false) + Importfile importfile = new() { - lines.Add(reader.ReadLine()); - } - fileContent = lines.AsReadOnly(); - ApplicationOptions = applicationOptions; - Analyse(); - } - - private void Analyse() - { + FileType = ImportFileType.Cuesheet + }; try { - Cuesheet = new Cuesheet(); + importfile.AnalysedCuesheet = new(); var cuesheetArtistGroupName = "CuesheetArtist"; var cuesheetTitleGroupName = "CuesheetTitle"; var cuesheetFileNameGroupName = "CuesheetFileName"; @@ -86,10 +54,10 @@ private void Analyse() var regexTrackPostGap = new Regex(CuesheetConstants.TrackPostGap + "(?'" + trackPostGapGroupName + "'.{0,})"); var regexCDTextfile = new Regex("^" + CuesheetConstants.CuesheetCDTextfile + " \"(?'" + cuesheetCDTextfileGroupName + "'.{0,})\""); var regexCatalogueNumber = new Regex("^" + CuesheetConstants.CuesheetCatalogueNumber + " (?'" + cuesheetCatalogueNumberGroupName + "'.{0,})"); - Track? track = null; + ImportTrack? track = null; List lines = []; List? recognizedLines = []; - foreach (var line in FileContent) + foreach (var line in fileContent) { lines.Add(line); String? recognizedLine = line; @@ -103,7 +71,7 @@ private void Analyse() if (matchGroup != null) { var artist = matchGroup.Value; - Cuesheet.Artist = artist; + importfile.AnalysedCuesheet.Artist = artist; } else { @@ -118,7 +86,7 @@ private void Analyse() if (matchGroup != null) { var title = matchGroup.Value; - Cuesheet.Title = title; + importfile.AnalysedCuesheet.Title = title; } else { @@ -133,7 +101,7 @@ private void Analyse() if (matchGroup != null) { var audioFile = matchGroup.Value; - Cuesheet.Audiofile = new Audiofile(audioFile); + importfile.AnalysedCuesheet.Audiofile = audioFile; } else { @@ -148,7 +116,7 @@ private void Analyse() if (matchGroup != null) { var cdTextfile = matchGroup.Value; - Cuesheet.CDTextfile = new CDTextfile(cdTextfile); + importfile.AnalysedCuesheet.CDTextfile = cdTextfile; } else { @@ -163,7 +131,7 @@ private void Analyse() if (matchGroup != null) { var catalogueNumber = matchGroup.Value; - Cuesheet.Cataloguenumber.Value = catalogueNumber; + importfile.AnalysedCuesheet.Cataloguenumber = catalogueNumber; } else { @@ -172,7 +140,7 @@ private void Analyse() } if (regexTrackBegin.IsMatch(line) == true) { - track = new Track(); + track = new ImportTrack(); recognizedLine = String.Format(CuesheetConstants.RecognizedMarkHTML, line); } if ((regexTrackArtist.IsMatch(line) == true) && (track != null)) @@ -277,7 +245,7 @@ private void Analyse() } if (track != null) { - Cuesheet.AddTrack(track, ApplicationOptions); + importfile.AnalysedCuesheet.Tracks.Add(track); } else { @@ -311,16 +279,16 @@ private void Analyse() } recognizedLines.Add(recognizedLine); } - fileContent = lines.AsReadOnly(); - FileContentRecognized = recognizedLines.AsReadOnly(); + importfile.FileContent = lines.AsReadOnly(); + importfile.FileContentRecognized = recognizedLines.AsReadOnly(); } catch (Exception ex) { - AnalyseException = ex; - Cuesheet = null; - FileContentRecognized = FileContent; + importfile.AnalyseException = ex; + importfile.AnalysedCuesheet = null; + importfile.FileContentRecognized = fileContent; } - AnalysisFinished?.Invoke(this, EventArgs.Empty); + return importfile; } } } diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs new file mode 100644 index 00000000..8eca0d48 --- /dev/null +++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs @@ -0,0 +1,204 @@ +//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 +//. +using AudioCuesheetEditor.Data.Options; +using AudioCuesheetEditor.Extensions; +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Model.IO; +using AudioCuesheetEditor.Model.IO.Audio; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Model.Utility; +using Blazorise; + +namespace AudioCuesheetEditor.Services.IO +{ + public enum ImportFileType + { + Unknown, + ProjectFile, + Cuesheet, + Textfile, + Audiofile + } + public class ImportManager(SessionStateContainer sessionStateContainer, ILocalStorageOptionsProvider localStorageOptionsProvider, TextImportService textImportService, TraceChangeManager traceChangeManager) + { + private readonly SessionStateContainer _sessionStateContainer = sessionStateContainer; + private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider = localStorageOptionsProvider; + private readonly TextImportService _textImportService = textImportService; + private readonly TraceChangeManager _traceChangeManager = traceChangeManager; + + public async Task> ImportFilesAsync(IEnumerable files) + { + Dictionary importFileTypes = []; + foreach (var file in files) + { + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Projectfile, FileExtensions.Projectfile)) + { + var fileContent = await ReadFileContentAsync(file); + var cuesheet = Projectfile.ImportFile(fileContent.ToArray()); + if (cuesheet != null) + { + _sessionStateContainer.ImportCuesheet = cuesheet; + } + importFileTypes.Add(file, ImportFileType.ProjectFile); + } + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Cuesheet, FileExtensions.Cuesheet)) + { + var fileContent = await ReadFileContentAsync(file); + fileContent.Position = 0; + using var reader = new StreamReader(fileContent); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + await ImportCuesheetAsync(lines); + importFileTypes.Add(file, ImportFileType.Cuesheet); + } + if (IOUtility.CheckFileMimeType(file, FileMimeTypes.Text, FileExtensions.Text)) + { + var fileContent = await ReadFileContentAsync(file); + fileContent.Position = 0; + using var reader = new StreamReader(fileContent); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + await ImportTextAsync([.. lines]); + importFileTypes.Add(file, ImportFileType.Textfile); + } + } + return importFileTypes; + } + + public async Task ImportTextAsync(IEnumerable fileContent) + { + var options = await _localStorageOptionsProvider.GetOptions(); + _sessionStateContainer.Importfile = _textImportService.Analyse(options, fileContent); + if (_sessionStateContainer.Importfile.AnalysedCuesheet != null) + { + var importCuesheet = new Cuesheet(); + await CopyCuesheetAsync(importCuesheet, _sessionStateContainer.Importfile.AnalysedCuesheet); + _sessionStateContainer.ImportCuesheet = importCuesheet; + } + } + + public async Task ImportCuesheetAsync() + { + if (_sessionStateContainer.ImportCuesheet != null) + { + _traceChangeManager.BulkEdit = true; + await CopyCuesheetAsync(_sessionStateContainer.Cuesheet, _sessionStateContainer.ImportCuesheet); + _traceChangeManager.BulkEdit = false; + } + _sessionStateContainer.ResetImport(); + } + + private async Task ImportCuesheetAsync(IEnumerable fileContent) + { + _sessionStateContainer.Importfile = CuesheetImportService.Analyse(fileContent); + if (_sessionStateContainer.Importfile.AnalysedCuesheet != null) + { + var importCuesheet = new Cuesheet(); + await CopyCuesheetAsync(importCuesheet, _sessionStateContainer.Importfile.AnalysedCuesheet); + _sessionStateContainer.ImportCuesheet = importCuesheet; + } + } + + private static async Task ReadFileContentAsync(IFileEntry file) + { + var fileContent = new MemoryStream(); + var stream = file.OpenReadStream(); + await stream.CopyToAsync(fileContent); + stream.Close(); + return fileContent; + } + + private async Task CopyCuesheetAsync(Cuesheet target, ICuesheet cuesheetToCopy) + { + target.IsImporting = true; + target.Artist = cuesheetToCopy.Artist; + target.Title = cuesheetToCopy.Title; + IEnumerable? tracks = null; + if (cuesheetToCopy is Cuesheet originCuesheet) + { + tracks = originCuesheet.Tracks; + // Copy sections + foreach (var section in originCuesheet.Sections) + { + var newSplitPoint = target.AddSection(); + newSplitPoint.CopyValues(section); + } + target.Audiofile = originCuesheet.Audiofile; + target.CDTextfile = originCuesheet.CDTextfile; + target.Cataloguenumber = originCuesheet.Cataloguenumber; + } + if (cuesheetToCopy is ImportCuesheet importCuesheet) + { + tracks = importCuesheet.Tracks; + if (String.IsNullOrEmpty(importCuesheet.Audiofile) == false) + { + target.Audiofile = new Audiofile(importCuesheet.Audiofile); + } + if (String.IsNullOrEmpty(importCuesheet.CDTextfile) == false) + { + target.CDTextfile = new CDTextfile(importCuesheet.CDTextfile); + } + target.Cataloguenumber = new Cataloguenumber() + { + Value = importCuesheet.Cataloguenumber + }; + } + if (tracks != null) + { + var applicationOptions = await _localStorageOptionsProvider.GetOptions(); + var begin = TimeSpan.Zero; + for (int i = 0; i < tracks.Count(); i++) + { + var importTrack = tracks.ElementAt(i); + //We don't want to copy the cuesheet reference since we are doing a copy and want to assign the track to this object + var track = new Track(importTrack, false); + if (importTrack is ImportTrack importTrackReference) + { + if (importTrackReference.StartDateTime != null) + { + if (i < tracks.Count() - 1) + { + var nextTrack = (ImportTrack)tracks.ElementAt(i + 1); + var length = nextTrack.StartDateTime - importTrackReference.StartDateTime; + track.Begin = begin; + track.End = begin + length; + if (track.End.HasValue) + { + begin = track.End.Value; + } + } + } + } + target.AddTrack(track, applicationOptions); + } + } + else + { + throw new NullReferenceException(); + } + target.IsImporting = false; + _sessionStateContainer.FireCuesheetImported(); + } + } +} diff --git a/AudioCuesheetEditor/Services/IO/TextImportService.cs b/AudioCuesheetEditor/Services/IO/TextImportService.cs new file mode 100644 index 00000000..c90ce366 --- /dev/null +++ b/AudioCuesheetEditor/Services/IO/TextImportService.cs @@ -0,0 +1,168 @@ +//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 +//. + +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.AudioCuesheet.Import; +using AudioCuesheetEditor.Model.IO.Audio; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; +using System.Reflection; +using System.Text.RegularExpressions; + +namespace AudioCuesheetEditor.Services.IO +{ + public class TextImportService + { + public ImportOptions? ImportOptions { get; private set; } + public IImportfile Analyse(ImportOptions importOptions, IEnumerable fileContent) + { + Importfile importfile = new() + { + FileType = ImportFileType.Textfile + }; + try + { + importfile.FileContent = fileContent; + ImportOptions = importOptions; + importfile.AnalysedCuesheet = new ImportCuesheet(); + Boolean cuesheetRecognized = false; + List recognizedFileContent = []; + foreach (var line in fileContent) + { + var recognizedLine = line; + if (String.IsNullOrEmpty(line) == false) + { + Boolean recognized = false; + if ((recognized == false) && (cuesheetRecognized == false) && (String.IsNullOrEmpty(ImportOptions.TextImportScheme.SchemeCuesheet) == false)) + { + //Remove entity names + var expression = ImportOptions.TextImportScheme.SchemeCuesheet.Replace(String.Format("{0}.", nameof(Cuesheet)), String.Empty).Replace(String.Format("{0}.", nameof(Track)), String.Empty); + var regExCuesheet = new Regex(expression); + recognizedLine = AnalyseLine(line, importfile.AnalysedCuesheet, regExCuesheet); + recognized = recognizedLine != null; + cuesheetRecognized = recognizedLine != null; + } + if ((recognized == false) && (String.IsNullOrEmpty(ImportOptions.TextImportScheme.SchemeTracks) == false)) + { + //Remove entity names + var expression = ImportOptions.TextImportScheme.SchemeTracks.Replace(String.Format("{0}.", nameof(Cuesheet)), String.Empty).Replace(String.Format("{0}.", nameof(Track)), String.Empty); + var regExTracks = new Regex(expression); + var track = new ImportTrack(); + recognizedLine = AnalyseLine(line, track, regExTracks); + recognized = recognizedLine != null; + importfile.AnalysedCuesheet.Tracks.Add(track); + } + } + recognizedFileContent.Add(recognizedLine); + } + if (recognizedFileContent.Count > 0) + { + importfile.FileContentRecognized = recognizedFileContent.AsReadOnly(); + } + } + catch (Exception ex) + { + importfile.FileContentRecognized = fileContent; + importfile.AnalyseException = ex; + importfile.AnalysedCuesheet = null; + } + return importfile; + } + + private String? AnalyseLine(String line, object entity, Regex regex) + { + String? recognized = null; + string? recognizedLine = line; + if (String.IsNullOrEmpty(line) == false) + { + var match = regex.Match(line); + if (match.Success) + { + for (int groupCounter = 1; groupCounter < match.Groups.Count; groupCounter++) + { + var key = match.Groups.Keys.ElementAt(groupCounter); + var group = match.Groups.GetValueOrDefault(key); + if ((group != null) && (group.Success)) + { + var property = entity.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (property != null) + { + SetValue(entity, property, group.Value); + recognizedLine = string.Concat(recognizedLine.AsSpan(0, group.Index + (13 * (groupCounter - 1))) + , String.Format(CuesheetConstants.RecognizedMarkHTML, group.Value) + , recognizedLine.AsSpan(group.Index + (13 * (groupCounter - 1)) + group.Length)); + } + else + { + throw new NullReferenceException(String.Format("Property '{0}' was not found for line content {1}", key, line)); + } + } + else + { + throw new NullReferenceException(String.Format("Group '{0}' could not be found!", key)); + } + } + if (recognizedLine.Contains(CuesheetConstants.RecognizedMarkHTML.Substring(0, CuesheetConstants.RecognizedMarkHTML.IndexOf("{0}")))) + { + recognized = recognizedLine; + } + } + else + { + recognized = line; + } + } + return recognized; + } + + private void SetValue(object entity, PropertyInfo property, string value) + { + if (property.PropertyType == typeof(TimeSpan?)) + { + property.SetValue(entity, TimeSpanUtility.ParseTimeSpan(value, ImportOptions?.TimeSpanFormat)); + } + if (property.PropertyType == typeof(uint?)) + { + property.SetValue(entity, Convert.ToUInt32(value)); + } + if (property.PropertyType == typeof(String)) + { + property.SetValue(entity, value); + } + if (property.PropertyType == typeof(IReadOnlyCollection)) + { + var list = Flag.AvailableFlags.Where(x => value.Contains(x.CuesheetLabel)); + ((ITrack)entity).SetFlags(list); + } + if (property.PropertyType == typeof(Audiofile)) + { + property.SetValue(entity, new Audiofile(value)); + } + if (property.PropertyType == typeof(Cataloguenumber)) + { + ((Cuesheet)entity).Cataloguenumber.Value = value; + } + if (property.PropertyType == typeof(DateTime?)) + { + if (DateTime.TryParse(value, out var date)) + { + property.SetValue(entity, date); + } + } + } + } +} diff --git a/AudioCuesheetEditor/Model/Utility/DateTimeUtility.cs b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs similarity index 55% rename from AudioCuesheetEditor/Model/Utility/DateTimeUtility.cs rename to AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs index 463c5ac0..f6b16cd9 100644 --- a/AudioCuesheetEditor/Model/Utility/DateTimeUtility.cs +++ b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs @@ -15,58 +15,26 @@ //. using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; using System.Linq.Expressions; using System.Reflection; -namespace AudioCuesheetEditor.Model.Utility +namespace AudioCuesheetEditor.Services.UI { - public class DateTimeUtility : IDisposable + public class ApplicationOptionsTimeSpanParser { - private readonly LocalStorageOptionsProvider? _localStorageOptionsProvider; - private readonly TimeSpanFormat? _timeFormat; + private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider; private ApplicationOptions? applicationOptions; private bool disposedValue; - public DateTimeUtility(LocalStorageOptionsProvider localStorageOptionsProvider) + public ApplicationOptionsTimeSpanParser(ILocalStorageOptionsProvider localStorageOptionsProvider) { _localStorageOptionsProvider = localStorageOptionsProvider; _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; Task.Run(InitAsync); } - public DateTimeUtility(TimeSpanFormat timeSpanFormat) - { - _timeFormat = timeSpanFormat; - } - - public void Dispose() - { - // Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "Dispose(bool disposing)" ein. - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - - public TimeSpan? ParseTimeSpan(String input) - { - TimeSpan? result = null; - if (String.IsNullOrEmpty(input) == false) - { - if (TimeSpanFormat?.Scheme == null) - { - if (TimeSpan.TryParse(input, out var parsed)) - { - result = parsed; - } - } - else - { - result = TimeSpanFormat.ParseTimeSpan(input); - } - } - return result; - } - public async Task TimespanTextChanged(T entity, Expression> expression, String value) { if (expression.Body is not MemberExpression memberExpression) @@ -77,7 +45,7 @@ public async Task TimespanTextChanged(T entity, Expression(T entity, Expression(); - } + applicationOptions ??= await _localStorageOptionsProvider.GetOptions(); } private void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions options) @@ -119,23 +82,5 @@ private void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions op applicationOptions = applicationOption; } } - - protected virtual void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // Verwalteten Zustand (verwaltete Objekte) bereinigen - if (_localStorageOptionsProvider != null) - { - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; - } - } - - disposedValue = true; - } - } - } } diff --git a/AudioCuesheetEditor/Shared/AudioPlayer.razor b/AudioCuesheetEditor/Shared/AudioPlayer.razor index 5d0ca5eb..ee987d0e 100644 --- a/AudioCuesheetEditor/Shared/AudioPlayer.razor +++ b/AudioCuesheetEditor/Shared/AudioPlayer.razor @@ -15,8 +15,7 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> - -@implements IDisposable +@implements IAsyncDisposable @inject ITextLocalizer _localizer @inject IHowl _howl @@ -77,7 +76,7 @@ along with Foobar. If not, see @code { - Timer audioUpdateTimer = default!; + Timer? audioUpdateTimer; int soundId; Track? currentlyPlayingTrack; HotKeysContext? hotKeysContext; @@ -92,20 +91,23 @@ along with Foobar. If not, see public TimeSpan? TotalTime { get; private set; } public Boolean AudioIsPlaying { get; private set; } - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _howl.OnPlay -= HowlOnPlay; _howl.OnPause -= HowlOnPause; _howl.OnEnd -= HowlOnEnd; _howl.OnStop -= HowlOnStop; - audioUpdateTimer.Dispose(); + _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; if (_sessionStateContainer.Cuesheet != null) { _sessionStateContainer.Cuesheet.AudioFileChanged -= Cuesheet_AudioFileChanged; } - _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; - _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; + audioUpdateTimer?.Dispose(); + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } public Boolean PlaybackPossible @@ -242,7 +244,7 @@ along with Foobar. If not, see void HowlOnPlay(Howler.Blazor.Components.Events.HowlPlayEventArgs args) { paused = false; - audioUpdateTimer.Start(); + audioUpdateTimer?.Start(); } void HowlOnPause(Howler.Blazor.Components.Events.HowlEventArgs args) @@ -253,7 +255,7 @@ along with Foobar. If not, see void HowlOnEnd(Howler.Blazor.Components.Events.HowlEventArgs args) { paused = false; - audioUpdateTimer.Stop(); + audioUpdateTimer?.Stop(); CurrentlyPlayingTrack = null; CurrentPlaybackPosition = null; AudioIsPlaying = false; @@ -263,7 +265,7 @@ along with Foobar. If not, see void HowlOnStop(Howler.Blazor.Components.Events.HowlEventArgs args) { paused = false; - audioUpdateTimer.Stop(); + audioUpdateTimer?.Stop(); CurrentlyPlayingTrack = null; CurrentPlaybackPosition = null; AudioIsPlaying = false; diff --git a/AudioCuesheetEditor/Shared/CultureSelector.razor b/AudioCuesheetEditor/Shared/CultureSelector.razor index d8bb3c73..7d6566d2 100644 --- a/AudioCuesheetEditor/Shared/CultureSelector.razor +++ b/AudioCuesheetEditor/Shared/CultureSelector.razor @@ -21,7 +21,7 @@ along with Foobar. If not, see @inject ITextLocalizerService _localizationService @inject ITextLocalizer _localizer @inject ILogger _logger -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider diff --git a/AudioCuesheetEditor/Shared/EditImportOptions.razor b/AudioCuesheetEditor/Shared/EditImportOptions.razor new file mode 100644 index 00000000..961e45be --- /dev/null +++ b/AudioCuesheetEditor/Shared/EditImportOptions.razor @@ -0,0 +1,179 @@ + + +@implements IDisposable + +@inject ITextLocalizerService _localizationService +@inject ITextLocalizer _localizer +@inject ITextLocalizer _validationMessageLocalizer +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider + + + + + + + @_localizer["Textimportscheme cuesheet"] + + + + + + + + + + + + + + + @_localizer["Select placeholder"] + + + @foreach (var availableSchemeTrack in TextImportScheme.AvailableSchemeCuesheet) + { + @_localizer[availableSchemeTrack.Key] + } + + + + + + + + + + + + @_localizer["Textimportscheme track"] + + + + + + + + + + + + + + + @_localizer["Select placeholder"] + + + @foreach (var availableSchemeTrack in TextImportScheme.AvailableSchemesTrack) + { + @_localizer[availableSchemeTrack.Key] + } + + + + + + + + + + + + @_localizer["Customized timespan format import"] + + + + + + + + + + + + + + + @_localizer["Select placeholder"] + + + @foreach (var availableFormat in TimeSpanFormat.AvailableTimespanScheme) + { + @_localizer[availableFormat.Key] + } + + + + + + + + + +@code{ + [Parameter] + public EventCallback OptionsChanged { get; set; } + + public ImportOptions? ImportOptions { get; private set; } + + public void Dispose() + { + _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionsSaved; + } + + protected override async Task OnInitializedAsync() + { + TimeSpanFormat.TextLocalizer = _localizer; + TextImportScheme.TextLocalizer = _localizer; + + _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; + _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionsSaved; + + ImportOptions = await _localStorageOptionsProvider.GetOptions(); + + await base.OnInitializedAsync(); + } + + void LocalizationService_LocalizationChanged(object? sender, EventArgs args) + { + TimeSpanFormat.TextLocalizer = _localizer; + TextImportScheme.TextLocalizer = _localizer; + StateHasChanged(); + } + + void LocalStorageOptionsProvider_OptionsSaved(object? sender, IOptions options) + { + if (options is ImportOptions importOptions) + { + ImportOptions = importOptions; + StateHasChanged(); + } + } + + async Task TextChangedAsync(Action setter, string text) + { + if (ImportOptions == null) + { + throw new NullReferenceException(); + } + setter(ImportOptions, text); + await _localStorageOptionsProvider.SaveOptions(ImportOptions); + await OptionsChanged.InvokeAsync(ImportOptions); + } +} \ No newline at end of file diff --git a/AudioCuesheetEditor/Shared/EditRecordOptions.razor b/AudioCuesheetEditor/Shared/EditRecordOptions.razor index 4414f1f0..e96e7037 100644 --- a/AudioCuesheetEditor/Shared/EditRecordOptions.razor +++ b/AudioCuesheetEditor/Shared/EditRecordOptions.razor @@ -18,7 +18,7 @@ along with Foobar. If not, see @implements IDisposable @inject ITextLocalizer _localizer -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject ITextLocalizerService _localizationService @inject ITextLocalizer _validationMessageLocalizer diff --git a/AudioCuesheetEditor/Shared/EditTrackModal.razor b/AudioCuesheetEditor/Shared/EditTrackModal.razor index b0b83d9c..84f5e846 100644 --- a/AudioCuesheetEditor/Shared/EditTrackModal.razor +++ b/AudioCuesheetEditor/Shared/EditTrackModal.razor @@ -15,17 +15,16 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> - -@implements IDisposable +@implements IAsyncDisposable @inject ITextLocalizer _localizer @inject MusicBrainzDataProvider _musicBrainzDataProvider @inject SessionStateContainer _sessionStateContainer -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject TraceChangeManager _traceChangeManager @inject HotKeys _hotKeys @inject ITextLocalizer _validationMessageLocalizer -@inject DateTimeUtility _dateTimeUtility +@inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser @@ -102,7 +101,7 @@ along with Foobar. If not, see @_localizer["Begin"] - + @@ -114,7 +113,7 @@ along with Foobar. If not, see @_localizer["End"] - + @@ -126,7 +125,7 @@ along with Foobar. If not, see @_localizer["Length"] - + @@ -149,7 +148,7 @@ along with Foobar. If not, see @_localizer["PreGap"] - + @@ -161,7 +160,7 @@ along with Foobar. If not, see @_localizer["PostGap"] - + @@ -253,7 +252,7 @@ along with Foobar. If not, see - + @@ -271,7 +270,7 @@ along with Foobar. If not, see - + @@ -289,7 +288,7 @@ along with Foobar. If not, see - + @@ -319,7 +318,7 @@ along with Foobar. If not, see - + @@ -337,7 +336,7 @@ along with Foobar. If not, see - + @@ -420,10 +419,13 @@ along with Foobar. If not, see Validations? validations; ApplicationOptions? applicationOptions; - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } protected override async Task OnInitializedAsync() diff --git a/AudioCuesheetEditor/Shared/MainLayout.razor b/AudioCuesheetEditor/Shared/MainLayout.razor index b79f7e52..ea35a241 100644 --- a/AudioCuesheetEditor/Shared/MainLayout.razor +++ b/AudioCuesheetEditor/Shared/MainLayout.razor @@ -18,7 +18,7 @@ along with Foobar. If not, see @inherits LayoutComponentBase -@implements IDisposable +@implements IAsyncDisposable @inject NavigationManager _navigationManager @inject ITextLocalizer _localizer @@ -27,7 +27,7 @@ along with Foobar. If not, see @inject ILogger _logger @inject IJSRuntime _jsRuntime @inject HotKeys _hotKeys -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject SessionStateContainer _sessionStateContainer @inject IBlazorDownloadFileService _blazorDownloadFileService @inject ITextLocalizer _validationMessageLocalizer @@ -484,9 +484,8 @@ along with Foobar. If not, see await base.OnInitializedAsync(); } - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; _traceChangeManager.TracedObjectHistoryChanged -= TraceChangeManager_TracedObjectHistoryChanged; @@ -500,6 +499,10 @@ along with Foobar. If not, see { modalExportdialogCuesheet.GenerateExportfilesClicked -= ModalExportdialogCuesheet_GenerateExportfilesClicked; } + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } public void SetDisplayMenuBar(Boolean display) diff --git a/AudioCuesheetEditor/Shared/ModalDialog.razor b/AudioCuesheetEditor/Shared/ModalDialog.razor index c19fc165..8355b413 100644 --- a/AudioCuesheetEditor/Shared/ModalDialog.razor +++ b/AudioCuesheetEditor/Shared/ModalDialog.razor @@ -15,8 +15,7 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> - -@implements IDisposable +@implements IAsyncDisposable @inject ITextLocalizer _localizer @inject HotKeys _hotKeys @@ -65,10 +64,13 @@ along with Foobar. If not, see @code { - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } protected override Task OnInitializedAsync() diff --git a/AudioCuesheetEditor/Shared/ModalExportdialog.razor b/AudioCuesheetEditor/Shared/ModalExportdialog.razor index 0d7fe465..fd83b9df 100644 --- a/AudioCuesheetEditor/Shared/ModalExportdialog.razor +++ b/AudioCuesheetEditor/Shared/ModalExportdialog.razor @@ -15,12 +15,12 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> -@implements IDisposable +@implements IAsyncDisposable @inject ITextLocalizer _localizer @inject ILogger _logger @inject ITextLocalizer _validationMessageLocalizer -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject SessionStateContainer _sessionStateContainer @inject IBlazorDownloadFileService _blazorDownloadFileService @inject HotKeys _hotKeys @@ -198,10 +198,13 @@ along with Foobar. If not, see await base.OnInitializedAsync(); } - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } public async Task Show() diff --git a/AudioCuesheetEditor/Shared/OptionsDialog.razor b/AudioCuesheetEditor/Shared/OptionsDialog.razor index 94820ff4..a0491b87 100644 --- a/AudioCuesheetEditor/Shared/OptionsDialog.razor +++ b/AudioCuesheetEditor/Shared/OptionsDialog.razor @@ -15,10 +15,9 @@ You should have received a copy of the GNU General Public License along with Foobar. If not, see . --> +@implements IAsyncDisposable -@implements IDisposable - -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject ITextLocalizer _localizer @inject ILogger _logger @inject IJSRuntime _jsRuntime @@ -159,11 +158,14 @@ along with Foobar. If not, see HotKeysContext? hotKeysContext; Validation? timespanformatValidation; - public void Dispose() + public async ValueTask DisposeAsync() { - hotKeysContext?.Dispose(); _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + if (hotKeysContext != null) + { + await hotKeysContext.DisposeAsync(); + } } public async Task Show() diff --git a/AudioCuesheetEditor/Shared/TracksTable.razor b/AudioCuesheetEditor/Shared/TracksTable.razor index 6252e512..61947058 100644 --- a/AudioCuesheetEditor/Shared/TracksTable.razor +++ b/AudioCuesheetEditor/Shared/TracksTable.razor @@ -20,13 +20,13 @@ along with Foobar. If not, see @inject ITextLocalizer _localizer @inject SessionStateContainer _sessionStateContainer -@inject LocalStorageOptionsProvider _localStorageOptionsProvider +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject TraceChangeManager _traceChangeManager @inject ILogger _logger @inject ITextLocalizerService _localizationService @inject MusicBrainzDataProvider _musicBrainzDataProvider @inject ITextLocalizer _validationMessageLocalizer -@inject DateTimeUtility _dateTimeUtility +@inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser @if (_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull) @@ -341,7 +341,7 @@ along with Foobar. If not, see } - + @@ -353,7 +353,7 @@ along with Foobar. If not, see - + @@ -364,7 +364,7 @@ along with Foobar. If not, see - + diff --git a/AudioCuesheetEditor/_Imports.razor b/AudioCuesheetEditor/_Imports.razor index f00ca655..b86c652b 100644 --- a/AudioCuesheetEditor/_Imports.razor +++ b/AudioCuesheetEditor/_Imports.razor @@ -24,6 +24,8 @@ @using AudioCuesheetEditor.Data.Options @using AudioCuesheetEditor.Model.Utility @using AudioCuesheetEditor.Data.Services +@using AudioCuesheetEditor.Services.IO +@using AudioCuesheetEditor.Services.UI @using Microsoft.Extensions.Logging @using Blazorise @using Blazorise.Components diff --git a/AudioCuesheetEditorTests/AudioCuesheetEditorTests.csproj b/AudioCuesheetEditorTests/AudioCuesheetEditorTests.csproj index e13033a2..c6ffcd8a 100644 --- a/AudioCuesheetEditorTests/AudioCuesheetEditorTests.csproj +++ b/AudioCuesheetEditorTests/AudioCuesheetEditorTests.csproj @@ -8,6 +8,7 @@ + diff --git a/AudioCuesheetEditorTests/Extensions/SessionStateContainerTests.cs b/AudioCuesheetEditorTests/Extensions/SessionStateContainerTests.cs index 87b97a64..885fe83e 100644 --- a/AudioCuesheetEditorTests/Extensions/SessionStateContainerTests.cs +++ b/AudioCuesheetEditorTests/Extensions/SessionStateContainerTests.cs @@ -1,13 +1,22 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AudioCuesheetEditor.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AudioCuesheetEditorTests.Utility; -using AudioCuesheetEditor.Model.UI; +//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 +//. using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditorTests.Utility; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AudioCuesheetEditor.Extensions.Tests { @@ -43,7 +52,7 @@ public void SessionStateContainerFireCuesheetChangedTest() cuesheetChangedFired = true; }; Assert.IsFalse(cuesheetChangedFired); - container.Cuesheet.Import(new Cuesheet(), helper.ApplicationOptions); + container.FireCuesheetImported(); Assert.IsTrue(cuesheetChangedFired); } } diff --git a/AudioCuesheetEditorTests/Model/AudioCuesheet/CuesheetTests.cs b/AudioCuesheetEditorTests/Model/AudioCuesheet/CuesheetTests.cs index 8e8cb927..00373876 100644 --- a/AudioCuesheetEditorTests/Model/AudioCuesheet/CuesheetTests.cs +++ b/AudioCuesheetEditorTests/Model/AudioCuesheet/CuesheetTests.cs @@ -13,18 +13,23 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. +using AudioCuesheetEditor.Data.Options; +using AudioCuesheetEditor.Extensions; using AudioCuesheetEditor.Model.Entity; -using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditor.Services.IO; using AudioCuesheetEditorTests.Properties; using AudioCuesheetEditorTests.Utility; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; +using System.Threading.Tasks; namespace AudioCuesheetEditor.Model.AudioCuesheet.Tests { @@ -213,65 +218,82 @@ public void MoveAndDeleteTrackTest() } [TestMethod()] - public void ImportTest() + public async Task ImportTestAsync() { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3"); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 01:15:54"); - - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.IsValid); + // Arrange + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); var testHelper = new TestHelper(); - var cuesheet = new Cuesheet(); - cuesheet.Import(textImportFile.Cuesheet, testHelper.ApplicationOptions); + // Act + await importManager.ImportTextAsync(fileContent); - Assert.IsNull(cuesheet.CDTextfile); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(0).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(1).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(2).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(3).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(4).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(5).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(6).Validate().Status); - Assert.AreEqual(ValidationStatus.Success, cuesheet.Tracks.ElementAt(7).Validate().Status); - - File.Delete(tempFile); + // Assert + Assert.IsNull(sessionStateContainer.ImportCuesheet?.CDTextfile); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(0).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(1).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(2).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(3).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(4).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(5).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(6).Validate().Status); + Assert.AreEqual(ValidationStatus.Success, sessionStateContainer.ImportCuesheet?.Tracks.ElementAt(7).Validate().Status); } [TestMethod()] - public void ImportTestCalculateEndCorrectly() + public async Task ImportTestCalculateEndCorrectlyAsync() { + // Arrange + var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug_54); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); var testHelper = new TestHelper(); - var textImportFile = new TextImportfile(new MemoryStream(Resources.Textimport_Bug_54)); - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 39); - Assert.IsTrue(textImportFile.IsValid); - var cuesheet = new Cuesheet(); - cuesheet.Import(textImportFile.Cuesheet, testHelper.ApplicationOptions); - Assert.IsTrue(cuesheet.Tracks.Count == 39); - Assert.IsTrue(cuesheet.Tracks.ElementAt(0).End == new TimeSpan(0, 5, 24)); - Assert.IsTrue(cuesheet.Tracks.ElementAt(38).Begin == new TimeSpan(3, 13, 13)); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions(); + importOptions.TextImportScheme.SchemeCuesheet = null; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + // Act + await importManager.ImportTextAsync(fileContent); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual(39, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(0, 5, 24), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).End); + Assert.AreEqual(new TimeSpan(3, 13, 13), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(38).Begin); } [TestMethod()] @@ -546,51 +568,59 @@ public void TrackLengthChangedWithIsLinkedToPreivousTest() Assert.AreEqual(editedTrack.End, track2.Begin); } [TestMethod()] - public void ImportSamplesTest() + public async Task ImportSamplesTestAsync() { - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes("../../../../AudioCuesheetEditor/wwwroot/samples/Sample_Inputfile.txt"))); - textImportFile.TextImportScheme.SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.IsValid); - + // Arrange + var fileContent = File.ReadAllLines("../../../../AudioCuesheetEditor/wwwroot/samples/Sample_Inputfile.txt"); var testHelper = new TestHelper(); - var cuesheet = new Cuesheet + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions { - Artist = "Testartist", - Title = "Testtitle" + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } }; - cuesheet.Import(textImportFile.Cuesheet, testHelper.ApplicationOptions); - Assert.AreEqual("CuesheetArtist", cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", cuesheet.Title); - Assert.AreEqual(8, cuesheet.Tracks.Count); - Assert.AreEqual(new TimeSpan(1, 15, 54), cuesheet.Tracks.Last().End); + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + // Act + await importManager.ImportTextAsync(fileContent); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual("CuesheetArtist", sessionStateContainer.ImportCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", sessionStateContainer.ImportCuesheet.Title); + Assert.AreEqual(8, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(1, 15, 54), sessionStateContainer.ImportCuesheet.Tracks.Last().End); } [TestMethod()] - public void ImportSamples2Test() + public async Task ImportSamples2TestAsync() { - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes("../../../../AudioCuesheetEditor/wwwroot/samples/Sample_Inputfile2.txt"))); - textImportFile.TextImportScheme.SchemeCuesheet = null; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.IsValid); - + // Arrange + var fileContent = File.ReadAllLines("../../../../AudioCuesheetEditor/wwwroot/samples/Sample_Inputfile2.txt"); var testHelper = new TestHelper(); - var cuesheet = new Cuesheet - { - Artist = "Testartist", - Title = "Testtitle" - }; - cuesheet.Import(textImportFile.Cuesheet, testHelper.ApplicationOptions); - Assert.IsNull(cuesheet.Artist); - Assert.IsNull(cuesheet.Title); - Assert.AreEqual(8, cuesheet.Tracks.Count); - Assert.AreEqual(new TimeSpan(1, 15, 54), cuesheet.Tracks.Last().End); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions(); + importOptions.TextImportScheme.SchemeCuesheet = null; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + // Act + await importManager.ImportTextAsync(fileContent); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Artist); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Title); + Assert.AreEqual(8, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(1, 15, 54), sessionStateContainer.ImportCuesheet.Tracks.Last().End); } [TestMethod()] diff --git a/AudioCuesheetEditorTests/Model/IO/Import/CuesheetImportfileTests.cs b/AudioCuesheetEditorTests/Model/IO/Import/CuesheetImportfileTests.cs deleted file mode 100644 index b401112a..00000000 --- a/AudioCuesheetEditorTests/Model/IO/Import/CuesheetImportfileTests.cs +++ /dev/null @@ -1,181 +0,0 @@ -//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 -//. -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AudioCuesheetEditor.Model.IO.Import; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using AudioCuesheetEditorTests.Utility; -using System.IO; -using AudioCuesheetEditorTests.Properties; -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditor.Model.Entity; - -namespace AudioCuesheetEditor.Model.IO.Import.Tests -{ - [TestClass()] - public class CuesheetImportfileTests - { - [TestMethod()] - public void CuesheetImportFileTest() - { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("PERFORMER \"Sample CD Artist\""); - builder.AppendLine("TITLE \"Sample CD Title\""); - builder.AppendLine("FILE \"AC DC - TNT.mp3\" MP3"); - builder.AppendLine("CDTEXTFILE \"Testfile.cdt\""); - builder.AppendLine("CATALOG 0123456789012"); - builder.AppendLine("TRACK 01 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 1\""); - builder.AppendLine(" TITLE \"Sample Title 1\""); - builder.AppendLine(" INDEX 01 00:00:00"); - builder.AppendLine("TRACK 02 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 2\""); - builder.AppendLine(" TITLE \"Sample Title 2\""); - builder.AppendLine(" INDEX 01 05:00:00"); - builder.AppendLine("TRACK 03 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 3\""); - builder.AppendLine(" TITLE \"Sample Title 3\""); - builder.AppendLine(" INDEX 01 09:23:00"); - builder.AppendLine("TRACK 04 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 4\""); - builder.AppendLine(" TITLE \"Sample Title 4\""); - builder.AppendLine(" INDEX 01 15:54:00"); - builder.AppendLine("TRACK 05 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 5\""); - builder.AppendLine(" TITLE \"Sample Title 5\""); - builder.AppendLine(" INDEX 01 20:13:00"); - builder.AppendLine("TRACK 06 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 6\""); - builder.AppendLine(" TITLE \"Sample Title 6\""); - builder.AppendLine(" INDEX 01 24:54:00"); - builder.AppendLine("TRACK 07 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 7\""); - builder.AppendLine(" TITLE \"Sample Title 7\""); - builder.AppendLine(" INDEX 01 31:54:00"); - builder.AppendLine("TRACK 08 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 8\""); - builder.AppendLine(" TITLE \"Sample Title 8\""); - builder.AppendLine(" INDEX 01 45:51:00"); - - var testHelper = new TestHelper(); - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - var importFile = new CuesheetImportfile(new MemoryStream(File.ReadAllBytes(tempFile)), testHelper.ApplicationOptions); - Assert.IsNull(importFile.AnalyseException); - Assert.IsNotNull(importFile.Cuesheet); - Assert.AreEqual(ValidationStatus.Success, importFile.Cuesheet.Validate().Status); - Assert.AreEqual(importFile.Cuesheet.Tracks.Count, 8); - Assert.IsNotNull(importFile.Cuesheet.CDTextfile); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample CD Artist\""), importFile.FileContentRecognized?.ElementAt(0)); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample CD Title\""), importFile.FileContentRecognized?.ElementAt(1)); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "FILE \"AC DC - TNT.mp3\" MP3"), importFile.FileContentRecognized?.ElementAt(2)); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "CDTEXTFILE \"Testfile.cdt\""), importFile.FileContentRecognized?.ElementAt(3)); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "CATALOG 0123456789012"), importFile.FileContentRecognized?.ElementAt(4)); - Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "TRACK 01 AUDIO"), importFile.FileContentRecognized?.ElementAt(5)); - Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample Artist 1\"")), importFile.FileContentRecognized?.ElementAt(6)); - Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample Title 1\"")), importFile.FileContentRecognized?.ElementAt(7)); - Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "INDEX 01 00:00:00")), importFile.FileContentRecognized?.ElementAt(8)); - - File.Delete(tempFile); - - importFile = new CuesheetImportfile(new MemoryStream(Resources.Playlist_Bug_30), testHelper.ApplicationOptions); - Assert.IsNull(importFile.AnalyseException); - Assert.IsNotNull(importFile.Cuesheet); - Assert.AreEqual(ValidationStatus.Success, importFile.Cuesheet.Validate().Status); - - importFile = new CuesheetImportfile(new MemoryStream(Resources.Playlist_Bug_57), testHelper.ApplicationOptions); - Assert.IsNull(importFile.AnalyseException); - Assert.IsNotNull(importFile.Cuesheet); - Assert.IsTrue(importFile.Cuesheet.Tracks.Count == 39); - Assert.AreEqual(importFile.Cuesheet.Tracks.ElementAt(24).Begin, new TimeSpan(2, 8, 21)); - - importFile = new CuesheetImportfile(new MemoryStream(Resources.Playlist__36_Frames), testHelper.ApplicationOptions); - Assert.IsNull(importFile.AnalyseException); - Assert.IsNotNull(importFile.Cuesheet); - Assert.IsTrue(importFile.Cuesheet.Tracks.Count == 12); - Assert.AreEqual(importFile.Cuesheet.Tracks.ElementAt(2).Begin, new TimeSpan(0, 0, 9, 15, 600)); - - builder = new StringBuilder(); - builder.AppendLine("PERFORMER \"Sample CD Artist\""); - builder.AppendLine("TITLE \"Sample CD Title\""); - builder.AppendLine("FILE \"AC DC - TNT.mp3\" MP3"); - builder.AppendLine("CDTEXTFILE \"Testfile.cdt\""); - builder.AppendLine("CATALOG 0123456789012"); - builder.AppendLine("TRACK 01 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 1\""); - builder.AppendLine(" TITLE \"Sample Title 1\""); - builder.AppendLine(" FLAGS 4CH DCP PRE SCMS"); - builder.AppendLine(" INDEX 01 00:00:00"); - builder.AppendLine("TRACK 02 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 2\""); - builder.AppendLine(" TITLE \"Sample Title 2\""); - builder.AppendLine(" FLAGS DCP PRE"); - builder.AppendLine(" INDEX 01 05:00:00"); - builder.AppendLine("TRACK 03 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 3\""); - builder.AppendLine(" TITLE \"Sample Title 3\""); - builder.AppendLine(" INDEX 01 09:23:00"); - builder.AppendLine("TRACK 04 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 4\""); - builder.AppendLine(" TITLE \"Sample Title 4\""); - builder.AppendLine(" INDEX 01 15:54:00"); - builder.AppendLine("TRACK 05 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 5\""); - builder.AppendLine(" TITLE \"Sample Title 5\""); - builder.AppendLine(" INDEX 01 20:13:00"); - builder.AppendLine(" POSTGAP 00:02:00"); - builder.AppendLine("TRACK 06 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 6\""); - builder.AppendLine(" TITLE \"Sample Title 6\""); - builder.AppendLine(" INDEX 01 24:54:00"); - builder.AppendLine("TRACK 07 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 7\""); - builder.AppendLine(" TITLE \"Sample Title 7\""); - builder.AppendLine(" PREGAP 00:04:00"); - builder.AppendLine(" INDEX 01 31:54:00"); - builder.AppendLine("TRACK 08 AUDIO"); - builder.AppendLine(" PERFORMER \"Sample Artist 8\""); - builder.AppendLine(" TITLE \"Sample Title 8\""); - builder.AppendLine(" INDEX 01 45:51:00"); - - tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - importFile = new CuesheetImportfile(new MemoryStream(File.ReadAllBytes(tempFile)), testHelper.ApplicationOptions); - - Assert.IsNull(importFile.AnalyseException); - Assert.IsNotNull(importFile.Cuesheet); - Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "FLAGS 4CH DCP PRE SCMS")), importFile.FileContentRecognized?.ElementAt(8)); - Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "PREGAP 00:04:00")), importFile.FileContentRecognized?.ElementAt(35)); - Assert.AreEqual(ValidationStatus.Success, importFile.Cuesheet.Validate().Status); - Assert.AreEqual(importFile.Cuesheet.Tracks.Count, 8); - Assert.IsNotNull(importFile.Cuesheet.CDTextfile); - Assert.IsTrue(importFile.Cuesheet.Tracks.ElementAt(0).Flags.Count == 4); - Assert.IsTrue(importFile.Cuesheet.Tracks.ElementAt(1).Flags.Count == 2); - Assert.IsNotNull(importFile.Cuesheet.Tracks.ElementAt(1).Flags.SingleOrDefault(x => x.CuesheetLabel == "DCP")); - Assert.IsNotNull(importFile.Cuesheet.Tracks.ElementAt(1).Flags.SingleOrDefault(x => x.CuesheetLabel == "PRE")); - Assert.AreEqual(new TimeSpan(0, 0, 2), importFile.Cuesheet.Tracks.ElementAt(4).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 4), importFile.Cuesheet.Tracks.ElementAt(6).PreGap); - - File.Delete(tempFile); - } - } -} \ No newline at end of file diff --git a/AudioCuesheetEditorTests/Model/IO/Import/TextImportfileTests.cs b/AudioCuesheetEditorTests/Model/IO/Import/TextImportfileTests.cs deleted file mode 100644 index e5f10684..00000000 --- a/AudioCuesheetEditorTests/Model/IO/Import/TextImportfileTests.cs +++ /dev/null @@ -1,344 +0,0 @@ -//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 -//. -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AudioCuesheetEditor.Model.IO; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; -using AudioCuesheetEditorTests.Utility; -using AudioCuesheetEditor.Model.IO.Import; -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditorTests.Properties; -using AudioCuesheetEditor.Model.Utility; -using AudioCuesheetEditor.Model.Options; - -namespace AudioCuesheetEditor.Model.IO.Import.Tests -{ - [TestClass()] - public class TextImportFileTests - { - [TestMethod()] - public void TextImportFileTest() - { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3"); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 01:15:54"); - - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.AreEqual("CuesheetArtist", textImportFile.Cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", textImportFile.Cuesheet.Title); - Assert.AreEqual("c:\\tmp\\Testfile.mp3", textImportFile.Cuesheet.Audiofile?.Name); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Artist, "Sample Artist 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Title, "Sample Title 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].End, new TimeSpan(0, 5, 0)); - - File.Delete(tempFile); - - //Prepare next Test - builder.Clear(); - - builder.AppendLine("CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt"); - builder.AppendLine("1|Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("2|Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("3|Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("4|Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("5|Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("6|Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("7|Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("8|Sample Artist 8 - Sample Title 8 01:15:54"); - - File.WriteAllText(tempFile, builder.ToString()); - - textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})"; - textImportFile.TextImportScheme.SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})"; - - Assert.IsNotNull(textImportFile.AnalyseException); - - textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeTracks = @"(?'Track.Position'\d{1,})[|](?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})"; - - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.AreEqual("CuesheetArtist", textImportFile.Cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", textImportFile.Cuesheet.Title); - Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", textImportFile.Cuesheet.CDTextfile?.Name); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ToArray()[5].Position == 6); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Artist, "Sample Artist 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Title, "Sample Title 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].End, new TimeSpan(0, 5, 0)); - - File.Delete(tempFile); - - //Prepare next Test - builder.Clear(); - - builder.AppendLine("CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt A83412346734"); - builder.AppendLine("1|Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("2|Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("3|Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("4|Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("5|Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("6|Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("7|Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("8|Sample Artist 8 - Sample Title 8 01:15:54"); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - builder.AppendLine(String.Empty); - - File.WriteAllText(tempFile, builder.ToString()); - - textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cuesheet.Cataloguenumber'.{1,})"; - textImportFile.TextImportScheme.SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})"; - - Assert.IsNotNull(textImportFile.AnalyseException); - - textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeTracks = @"(?'Track.Position'.{1,})[|](?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})"; - - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.AreEqual("CuesheetArtist", textImportFile.Cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", textImportFile.Cuesheet.Title); - Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", textImportFile.Cuesheet.CDTextfile?.Name); - Assert.AreEqual("A83412346734", textImportFile.Cuesheet.Cataloguenumber.Value); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ToArray()[5].Position == 6); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Artist, "Sample Artist 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Title, "Sample Title 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].End, new TimeSpan(0, 5, 0)); - - File.Delete(tempFile); - - //Prepare next test - - //Prepare text input file - builder = new StringBuilder(); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 01:15:54"); - builder.AppendLine("Sample Artist 9 - Sample Title 9"); - - tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 9); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Artist, "Sample Artist 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Title, "Sample Title 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[7].End, new TimeSpan(1, 15, 54)); - - File.Delete(tempFile); - - var importOptions = new ImportOptions() { TimeSpanFormat = new TimeSpanFormat() { Scheme = "(?'TimeSpanFormat.Minutes'\\d{1,})[:](?'TimeSpanFormat.Seconds'\\d{1,})" } }; - textImportFile = new TextImportfile(new MemoryStream(Resources.Textimport_Bug_213), importOptions); - textImportFile.TextImportScheme.SchemeCuesheet = "(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Track.Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'Track.End'.{1,})"; - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 4); - Assert.AreEqual(new TimeSpan(2, 3, 23), textImportFile.Cuesheet.Tracks.ToArray()[3].End); - } - - [TestMethod()] - public void TextImportFileTestFlags() - { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:05:00 DCP"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:15:54 PRE, DCP"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:20:13 4CH"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:31:54 PRE DCP 4CH"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 01:15:54 PRE DCP 4CH SCMS"); - - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - textImportFile.TextImportScheme.SchemeTracks = "(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Track.Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'Track.End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.Flags'[a-zA-Z 0-9,]{1,})"; - - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(0).Flags.Contains(AudioCuesheet.Flag.DCP)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(2).Flags.Contains(AudioCuesheet.Flag.DCP)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(2).Flags.Contains(AudioCuesheet.Flag.PRE)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(3).Flags.Contains(AudioCuesheet.Flag.FourCH)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(5).Flags.Contains(AudioCuesheet.Flag.FourCH)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(5).Flags.Contains(AudioCuesheet.Flag.PRE)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(5).Flags.Contains(AudioCuesheet.Flag.DCP)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(7).Flags.Contains(AudioCuesheet.Flag.DCP)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(7).Flags.Contains(AudioCuesheet.Flag.PRE)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(7).Flags.Contains(AudioCuesheet.Flag.FourCH)); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.ElementAt(7).Flags.Contains(AudioCuesheet.Flag.SCMS)); - - File.Delete(tempFile); - } - - [TestMethod()] - public void TextImportFileTestPreGapAndPostGap() - { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:00:02 00:05:00 00:00:00"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:00:04 00:09:23 00:00:00"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:00:00 00:15:54 00:00:02"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:00:00 00:20:13 00:00:03"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:00:00 00:24:54 00:00:04"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:00:00 00:31:54 00:00:01"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:00:00 00:45:54 00:00:00"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 00:00:02 01:15:54 00:00:00"); - - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - textImportFile.TextImportScheme.SchemeTracks = "(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Track.Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'Track.PreGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.PostGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})"; - - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.AreEqual(new TimeSpan(0, 0, 2), textImportFile.Cuesheet.Tracks.ElementAt(0).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(0).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 4), textImportFile.Cuesheet.Tracks.ElementAt(1).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(1).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(2).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 2), textImportFile.Cuesheet.Tracks.ElementAt(2).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(3).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 3), textImportFile.Cuesheet.Tracks.ElementAt(3).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(4).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 4), textImportFile.Cuesheet.Tracks.ElementAt(4).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(5).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 1), textImportFile.Cuesheet.Tracks.ElementAt(5).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(6).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(6).PostGap); - Assert.AreEqual(new TimeSpan(0, 0, 2), textImportFile.Cuesheet.Tracks.ElementAt(7).PreGap); - Assert.AreEqual(new TimeSpan(0, 0, 0), textImportFile.Cuesheet.Tracks.ElementAt(7).PostGap); - - File.Delete(tempFile); - } - - [TestMethod()] - public void FileContentRecognizedTests() - { - //Prepare text input file - StringBuilder builder = new(); - builder.AppendLine("CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3"); - builder.AppendLine("Sample Artist 1 - Sample Title 1 00:05:00"); - builder.AppendLine("Sample Artist 2 - Sample Title 2 00:09:23"); - builder.AppendLine("Sample Artist 3 - Sample Title 3 00:15:54"); - builder.AppendLine("Sample Artist 4 - Sample Title 4 00:20:13"); - builder.AppendLine("Sample Artist 5 - Sample Title 5 00:24:54"); - builder.AppendLine("Sample Artist 6 - Sample Title 6 00:31:54"); - builder.AppendLine("Sample Artist 7 - Sample Title 7 00:45:54"); - builder.AppendLine("Sample Artist 8 - Sample Title 8 01:15:54"); - - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, builder.ToString()); - - //Test TextImportFile - var textImportFile = new TextImportfile(new MemoryStream(File.ReadAllBytes(tempFile))); - textImportFile.TextImportScheme.SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet; - textImportFile.TextImportScheme.SchemeTracks = String.Empty; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsNotNull(textImportFile.FileContentRecognized); - Assert.AreEqual(String.Format("{0} - {1} {2}", - String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetArtist"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetTitle"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "c:\\tmp\\Testfile.mp3")), textImportFile.FileContentRecognized.First()); - Assert.AreEqual("CuesheetArtist", textImportFile.Cuesheet.Artist); - Assert.AreEqual("CuesheetTitle", textImportFile.Cuesheet.Title); - Assert.AreEqual("c:\\tmp\\Testfile.mp3", textImportFile.Cuesheet.Audiofile?.Name); - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 0); - Assert.AreEqual("Sample Artist 1 - Sample Title 1 00:05:00", textImportFile.FileContentRecognized.ElementAt(1)); - Assert.AreEqual("Sample Artist 2 - Sample Title 2 00:09:23", textImportFile.FileContentRecognized.ElementAt(2)); - Assert.AreEqual("Sample Artist 3 - Sample Title 3 00:15:54", textImportFile.FileContentRecognized.ElementAt(3)); - Assert.AreEqual("Sample Artist 4 - Sample Title 4 00:20:13", textImportFile.FileContentRecognized.ElementAt(4)); - Assert.AreEqual("Sample Artist 5 - Sample Title 5 00:24:54", textImportFile.FileContentRecognized.ElementAt(5)); - Assert.AreEqual("Sample Artist 6 - Sample Title 6 00:31:54", textImportFile.FileContentRecognized.ElementAt(6)); - Assert.AreEqual("Sample Artist 7 - Sample Title 7 00:45:54", textImportFile.FileContentRecognized.ElementAt(7)); - Assert.AreEqual("Sample Artist 8 - Sample Title 8 01:15:54", textImportFile.FileContentRecognized.ElementAt(8)); - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsTrue(textImportFile.Cuesheet.Tracks.Count == 8); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Artist, "Sample Artist 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].Title, "Sample Title 1"); - Assert.AreEqual(textImportFile.Cuesheet.Tracks.ToArray()[0].End, new TimeSpan(0, 5, 0)); - Assert.AreEqual(String.Format("{0} - {1} {2}", - String.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Artist 8"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Title 8"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "01:15:54")), textImportFile.FileContentRecognized.Last()); - - File.Delete(tempFile); - - textImportFile = new TextImportfile(new MemoryStream(Resources.Textimport_Bug__233)); - textImportFile.TextImportScheme.SchemeCuesheet = String.Empty; - textImportFile.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; - Assert.IsNull(textImportFile.AnalyseException); - Assert.IsNotNull(textImportFile.Cuesheet); - Assert.IsNotNull(textImportFile.FileContentRecognized); - Assert.AreEqual(String.Format("{0} - {1}\t\t\t\t\t\t\t\t{2}", - String.Format(CuesheetConstants.RecognizedMarkHTML, "Age Of Love"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "The Age Of Love (Charlotte De Witte & Enrico Sangiuliano Remix)"), - String.Format(CuesheetConstants.RecognizedMarkHTML, "04:29:28")), textImportFile.FileContentRecognized.ElementAt(53)); - } - } -} \ No newline at end of file diff --git a/AudioCuesheetEditorTests/Model/UI/TraceChangeManagerTests.cs b/AudioCuesheetEditorTests/Model/UI/TraceChangeManagerTests.cs index 7b86a8d3..fe31187a 100644 --- a/AudioCuesheetEditorTests/Model/UI/TraceChangeManagerTests.cs +++ b/AudioCuesheetEditorTests/Model/UI/TraceChangeManagerTests.cs @@ -1,16 +1,34 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using AudioCuesheetEditor.Model.UI; +//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 +//. +using AudioCuesheetEditor.Data.Options; +using AudioCuesheetEditor.Extensions; +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.Entity; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Services.IO; +using AudioCuesheetEditorTests.Properties; +using AudioCuesheetEditorTests.Utility; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; using System; using System.Collections.Generic; +using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; -using AudioCuesheetEditor.Model.AudioCuesheet; -using AudioCuesheetEditorTests.Utility; -using AudioCuesheetEditor.Model.IO.Import; -using System.IO; -using AudioCuesheetEditorTests.Properties; -using AudioCuesheetEditor.Model.Entity; namespace AudioCuesheetEditor.Model.UI.Tests { @@ -148,40 +166,123 @@ public void TrackListTest() } [TestMethod()] - public void ImportCuesheetTest() + public async Task Import_ValidTextfile_IsUndoable() { + // Arrange var testhelper = new TestHelper(); - var manager = new TraceChangeManager(TestHelper.CreateLogger()); - var textImportFile = new TextImportfile(new MemoryStream(Resources.Textimport_with_Cuesheetdata)); - textImportFile.TextImportScheme.SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})"; - var cuesheet = new Cuesheet(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions(); + importOptions.TextImportScheme.SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})"; + importOptions.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); Boolean eventFired = false; - cuesheet.TrackAdded += delegate + sessionStateContainer.Cuesheet.TrackAdded += delegate { eventFired = true; }; - manager.TraceChanges(cuesheet); - Assert.IsFalse(manager.CanUndo); - Assert.IsFalse(manager.CanRedo); - Assert.IsNotNull(textImportFile.Cuesheet); - cuesheet.Import(textImportFile.Cuesheet, testhelper.ApplicationOptions, manager); - Assert.AreEqual("DJFreezeT", cuesheet.Artist); - Assert.AreEqual("0123456789123", cuesheet.Cataloguenumber.Value); - Assert.AreNotEqual(0, cuesheet.Tracks.Count); - Assert.IsTrue(manager.CanUndo); - manager.Undo(); - Assert.AreEqual(0, cuesheet.Tracks.Count); - Assert.IsTrue(String.IsNullOrEmpty(cuesheet.Artist)); - Assert.IsTrue(String.IsNullOrEmpty(cuesheet.Cataloguenumber.Value)); - Assert.IsFalse(manager.CanUndo); - Assert.IsTrue(manager.CanRedo); - manager.Redo(); - Assert.AreEqual("DJFreezeT", cuesheet.Artist); - Assert.AreEqual("0123456789123", cuesheet.Cataloguenumber.Value); - Assert.AreNotEqual(0, cuesheet.Tracks.Count); + // Act + await importManager.ImportTextAsync(fileContent); + // Assert + Assert.IsFalse(traceChangeManager.CanUndo); + Assert.IsFalse(traceChangeManager.CanRedo); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual("DJFreezeT", sessionStateContainer.ImportCuesheet.Artist); + Assert.AreEqual("0123456789123", sessionStateContainer.ImportCuesheet.Cataloguenumber.Value); + Assert.AreNotEqual(0, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.IsFalse(eventFired); + } + + [TestMethod()] + public async Task UndoImport_ValidTextfile_ResetsToEmptyCuesheet() + { + // Arrange + var testhelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions(); + importOptions.TextImportScheme.SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})"; + importOptions.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + Boolean eventFired = false; + sessionStateContainer.Cuesheet.TrackAdded += delegate + { + eventFired = true; + }; + await importManager.ImportTextAsync(fileContent); + await importManager.ImportCuesheetAsync(); + // Act + traceChangeManager.Undo(); + // Assert + Assert.AreEqual(0, sessionStateContainer.Cuesheet.Tracks.Count); + Assert.IsTrue(String.IsNullOrEmpty(sessionStateContainer.Cuesheet.Artist)); + Assert.IsTrue(String.IsNullOrEmpty(sessionStateContainer.Cuesheet.Cataloguenumber.Value)); + Assert.IsFalse(traceChangeManager.CanUndo); + Assert.IsTrue(traceChangeManager.CanRedo); Assert.IsFalse(eventFired); } + [TestMethod()] + public async Task UndoAndRedoImport_ValidTextfile_ResetsTextfileValues() + { + // Arrange + var testhelper = new TestHelper(); + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var textImportMemoryStream = new MemoryStream(Resources.Textimport_with_Cuesheetdata); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions(); + importOptions.TextImportScheme.SchemeCuesheet = "(?'Artist'\\A.*) - (?'Title'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Cataloguenumber'.{1,})"; + importOptions.TextImportScheme.SchemeTracks = TextImportScheme.DefaultSchemeTracks; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + Boolean eventFired = false; + sessionStateContainer.Cuesheet.TrackAdded += delegate + { + eventFired = true; + }; + await importManager.ImportTextAsync(fileContent); + traceChangeManager.Undo(); + // Act + traceChangeManager.Redo(); + // Assert + Assert.AreEqual("DJFreezeT", sessionStateContainer.ImportCuesheet?.Artist); + Assert.AreEqual("0123456789123", sessionStateContainer.ImportCuesheet?.Cataloguenumber.Value); + Assert.AreEqual(39, sessionStateContainer.ImportCuesheet?.Tracks.Count); + Assert.IsFalse(eventFired); + + } + [TestMethod()] public void RemoveTracksTest() { diff --git a/AudioCuesheetEditorTests/Model/Utility/DateTimeUtilityTests.cs b/AudioCuesheetEditorTests/Model/Utility/TimeSpanUtilityTests.cs similarity index 75% rename from AudioCuesheetEditorTests/Model/Utility/DateTimeUtilityTests.cs rename to AudioCuesheetEditorTests/Model/Utility/TimeSpanUtilityTests.cs index 4da6ac1f..22dac02a 100644 --- a/AudioCuesheetEditorTests/Model/Utility/DateTimeUtilityTests.cs +++ b/AudioCuesheetEditorTests/Model/Utility/TimeSpanUtilityTests.cs @@ -19,45 +19,43 @@ namespace AudioCuesheetEditor.Model.Utility.Tests { [TestClass()] - public class DateTimeUtilityTests + public class TimeSpanUtilityTests { [TestMethod()] public void ParseTimeSpanTest() { - var utility = new DateTimeUtility(new TimeSpanFormat()); - var timespan = utility.ParseTimeSpan("01:23:45"); + var timespan = TimeSpanUtility.ParseTimeSpan("01:23:45"); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(1, 23, 45), timespan); var format = new TimeSpanFormat() { Scheme = "(?'Minutes'\\d{1,})[:](?'Seconds'\\d{1,})" }; - utility = new DateTimeUtility(format); - timespan = utility.ParseTimeSpan("3:12"); + timespan = TimeSpanUtility.ParseTimeSpan("3:12", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(0, 3, 12), timespan); - timespan = utility.ParseTimeSpan("63:12"); + timespan = TimeSpanUtility.ParseTimeSpan("63:12", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(1, 3, 12), timespan); format.Scheme = "(?'Hours'\\d{1,})[:](?'Minutes'\\d{1,})"; - timespan = utility.ParseTimeSpan("23:12"); + timespan = TimeSpanUtility.ParseTimeSpan("23:12", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(23, 12, 0), timespan); format.Scheme = "(?'Hours'\\d{1,})[:](?'Minutes'\\d{1,})[:](?'Seconds'\\d{1,})"; - timespan = utility.ParseTimeSpan("23:45:56"); + timespan = TimeSpanUtility.ParseTimeSpan("23:45:56", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(0, 23, 45, 56), timespan); format.Scheme = "(?'Days'\\d{1,})[.](?'Hours'\\d{1,})[:](?'Minutes'\\d{1,})[:](?'Seconds'\\d{1,})"; - timespan = utility.ParseTimeSpan("2.23:45:56"); + timespan = TimeSpanUtility.ParseTimeSpan("2.23:45:56", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(2, 23, 45, 56), timespan); format.Scheme = "(?'Hours'\\d{1,})[:](?'TimeSpanFormat.Minutes'\\d{1,})[:](?'TimeSpanFormat.Seconds'\\d{1,})[.](?'TimeSpanFormat.Milliseconds'\\d{1,})"; - timespan = utility.ParseTimeSpan("23:45:56.599"); + timespan = TimeSpanUtility.ParseTimeSpan("23:45:56.599", format); Assert.IsNotNull(timespan); Assert.AreEqual(new TimeSpan(0, 23, 45, 56, 599), timespan); - timespan = utility.ParseTimeSpan("1.2e:45:87.h3a"); + timespan = TimeSpanUtility.ParseTimeSpan("1.2e:45:87.h3a", format); Assert.IsNull(timespan); - timespan = utility.ParseTimeSpan("Test"); + timespan = TimeSpanUtility.ParseTimeSpan("Test", format); Assert.IsNull(timespan); format.Scheme = "this is a test"; - timespan = utility.ParseTimeSpan("Test"); + timespan = TimeSpanUtility.ParseTimeSpan("Test", format); Assert.IsNull(timespan); } } diff --git a/AudioCuesheetEditorTests/Services/IO/CuesheetImportServiceTests.cs b/AudioCuesheetEditorTests/Services/IO/CuesheetImportServiceTests.cs new file mode 100644 index 00000000..4bbcc7bb --- /dev/null +++ b/AudioCuesheetEditorTests/Services/IO/CuesheetImportServiceTests.cs @@ -0,0 +1,217 @@ +//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 +//. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditorTests.Properties; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AudioCuesheetEditor.Services.IO.Tests +{ + [TestClass()] + public class CuesheetImportServiceTests + { + [TestMethod()] + public void Analyse_WithSampleCuesheet_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "PERFORMER \"Sample CD Artist\"", + "TITLE \"Sample CD Title\"", + "FILE \"AC DC - TNT.mp3\" MP3", + "CDTEXTFILE \"Testfile.cdt\"", + "CATALOG 0123456789012", + "TRACK 01 AUDIO", + " PERFORMER \"Sample Artist 1\"", + " TITLE \"Sample Title 1\"", + " INDEX 01 00:00:00", + "TRACK 02 AUDIO", + " PERFORMER \"Sample Artist 2\"", + " TITLE \"Sample Title 2\"", + " INDEX 01 05:00:00", + "TRACK 03 AUDIO", + " PERFORMER \"Sample Artist 3\"", + " TITLE \"Sample Title 3\"", + " INDEX 01 09:23:00", + "TRACK 04 AUDIO", + " PERFORMER \"Sample Artist 4\"", + " TITLE \"Sample Title 4\"", + " INDEX 01 15:54:00", + "TRACK 05 AUDIO", + " PERFORMER \"Sample Artist 5\"", + " TITLE \"Sample Title 5\"", + " INDEX 01 20:13:00", + "TRACK 06 AUDIO", + " PERFORMER \"Sample Artist 6\"", + " TITLE \"Sample Title 6\"", + " INDEX 01 24:54:00", + "TRACK 07 AUDIO", + " PERFORMER \"Sample Artist 7\"", + " TITLE \"Sample Title 7\"", + " INDEX 01 31:54:00", + "TRACK 08 AUDIO", + " PERFORMER \"Sample Artist 8\"", + " TITLE \"Sample Title 8\"", + " INDEX 01 45:51:00" + }; + // Act + var importFile = CuesheetImportService.Analyse(fileContent); + // Assert + Assert.IsNotNull(importFile); + Assert.IsNull(importFile.AnalyseException); + Assert.IsNotNull(importFile.AnalysedCuesheet); + Assert.AreEqual(8, importFile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample CD Artist\""), importFile.FileContentRecognized?.ElementAt(0)); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample CD Title\""), importFile.FileContentRecognized?.ElementAt(1)); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "FILE \"AC DC - TNT.mp3\" MP3"), importFile.FileContentRecognized?.ElementAt(2)); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "CDTEXTFILE \"Testfile.cdt\""), importFile.FileContentRecognized?.ElementAt(3)); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "CATALOG 0123456789012"), importFile.FileContentRecognized?.ElementAt(4)); + Assert.AreEqual(String.Format(CuesheetConstants.RecognizedMarkHTML, "TRACK 01 AUDIO"), importFile.FileContentRecognized?.ElementAt(5)); + Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "PERFORMER \"Sample Artist 1\"")), importFile.FileContentRecognized?.ElementAt(6)); + Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "TITLE \"Sample Title 1\"")), importFile.FileContentRecognized?.ElementAt(7)); + Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "INDEX 01 00:00:00")), importFile.FileContentRecognized?.ElementAt(8)); + } + + [TestMethod()] + public void Analyse_WithCuesheetBug30_CreatesValidCuesheet() + { + //Arrange + var textImportMemoryStream = new MemoryStream(Resources.Playlist_Bug_30); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + //Act + var importFile = CuesheetImportService.Analyse(fileContent); + //Assert + Assert.IsNull(importFile.AnalyseException); + Assert.IsNotNull(importFile.AnalysedCuesheet); + } + + [TestMethod()] + public void Analyse_WithCuesheetBug57_CreatesValidCuesheet() + { + //Arrange + var textImportMemoryStream = new MemoryStream(Resources.Playlist_Bug_57); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + //Act + var importFile = CuesheetImportService.Analyse(fileContent); + //Assert + Assert.IsNull(importFile.AnalyseException); + Assert.IsNotNull(importFile.AnalysedCuesheet); + Assert.AreEqual(39, importFile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(2, 8, 21), importFile.AnalysedCuesheet.Tracks.ElementAt(24).Begin); + } + + [TestMethod()] + public void Analyse_WithCuesheetBug36_CreatesValidCuesheet() + { + //Arrange + var textImportMemoryStream = new MemoryStream(Resources.Playlist__36_Frames); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + //Act + var importFile = CuesheetImportService.Analyse(fileContent); + //Assert + Assert.IsNull(importFile.AnalyseException); + Assert.IsNotNull(importFile.AnalysedCuesheet); + Assert.AreEqual(12, importFile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(0, 0, 9, 15, 600), importFile.AnalysedCuesheet.Tracks.ElementAt(2).Begin); + } + + [TestMethod()] + public void Analyse_WithCDTextFileCatalogueNumberAndPreAndPostGap_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "PERFORMER \"Sample CD Artist\"", + "TITLE \"Sample CD Title\"", + "FILE \"AC DC - TNT.mp3\" MP3", + "CDTEXTFILE \"Testfile.cdt\"", + "CATALOG 0123456789012", + "TRACK 01 AUDIO", + " PERFORMER \"Sample Artist 1\"", + " TITLE \"Sample Title 1\"", + " FLAGS 4CH DCP PRE SCMS", + " INDEX 01 00:00:00", + "TRACK 02 AUDIO", + " PERFORMER \"Sample Artist 2\"", + " TITLE \"Sample Title 2\"", + " FLAGS DCP PRE", + " INDEX 01 05:00:00", + "TRACK 03 AUDIO", + " PERFORMER \"Sample Artist 3\"", + " TITLE \"Sample Title 3\"", + " INDEX 01 09:23:00", + "TRACK 04 AUDIO", + " PERFORMER \"Sample Artist 4\"", + " TITLE \"Sample Title 4\"", + " INDEX 01 15:54:00", + "TRACK 05 AUDIO", + " PERFORMER \"Sample Artist 5\"", + " TITLE \"Sample Title 5\"", + " INDEX 01 20:13:00", + " POSTGAP 00:02:00", + "TRACK 06 AUDIO", + " PERFORMER \"Sample Artist 6\"", + " TITLE \"Sample Title 6\"", + " INDEX 01 24:54:00", + "TRACK 07 AUDIO", + " PERFORMER \"Sample Artist 7\"", + " TITLE \"Sample Title 7\"", + " PREGAP 00:04:00", + " INDEX 01 31:54:00", + "TRACK 08 AUDIO", + " PERFORMER \"Sample Artist 8\"", + " TITLE \"Sample Title 8\"", + " INDEX 01 45:51:00" + }; + // Act + var importFile = CuesheetImportService.Analyse(fileContent); + // Assert + Assert.IsNull(importFile.AnalyseException); + Assert.IsNotNull(importFile.AnalysedCuesheet); + Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "FLAGS 4CH DCP PRE SCMS")), importFile.FileContentRecognized?.ElementAt(8)); + Assert.AreEqual(String.Format(" {0}", String.Format(CuesheetConstants.RecognizedMarkHTML, "PREGAP 00:04:00")), importFile.FileContentRecognized?.ElementAt(35)); + Assert.AreEqual(8, importFile.AnalysedCuesheet.Tracks.Count); + Assert.IsNotNull(importFile.AnalysedCuesheet.CDTextfile); + Assert.AreEqual(4, importFile.AnalysedCuesheet.Tracks.ElementAt(0).Flags.Count); + Assert.AreEqual(2, importFile.AnalysedCuesheet.Tracks.ElementAt(1).Flags.Count); + Assert.IsNotNull(importFile.AnalysedCuesheet.Tracks.ElementAt(1).Flags.SingleOrDefault(x => x.CuesheetLabel == "DCP")); + Assert.IsNotNull(importFile.AnalysedCuesheet.Tracks.ElementAt(1).Flags.SingleOrDefault(x => x.CuesheetLabel == "PRE")); + Assert.AreEqual(new TimeSpan(0, 0, 2), importFile.AnalysedCuesheet.Tracks.ElementAt(4).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 4), importFile.AnalysedCuesheet.Tracks.ElementAt(6).PreGap); + } + } +} \ No newline at end of file diff --git a/AudioCuesheetEditorTests/Services/IO/ImportManagerTests.cs b/AudioCuesheetEditorTests/Services/IO/ImportManagerTests.cs new file mode 100644 index 00000000..bd98e833 --- /dev/null +++ b/AudioCuesheetEditorTests/Services/IO/ImportManagerTests.cs @@ -0,0 +1,111 @@ +//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 +//. + +using AudioCuesheetEditor.Data.Options; +using AudioCuesheetEditor.Extensions; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.UI; +using AudioCuesheetEditorTests.Utility; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace AudioCuesheetEditor.Services.IO.Tests +{ + [TestClass()] + public class ImportManagerTests + { + [TestMethod()] + public async Task ImportTextAsync_TextfileWithStartDateTime_CreatesValidCuesheetAsync() + { + // Arrange + var fileContent = new List + { + "Innellea~The Golden Fort~02.08.2024 20:10:48", + "Nora En Pure~Diving with Whales (Daniel Portman Remix)~02.08.2024 20:15:21", + "WhoMadeWho & Adriatique~Miracle (RÜFÜS DU SOL Remix)~02.08.2024 20:20:42", + "Ella Wild~Poison D'araignee (Original Mix)~02.08.2024 20:28:03", + "Stil & Bense~On The Edge (Original Mix)~02.08.2024 20:32:42", + "Nebula~Clairvoyant Dreams~02.08.2024 20:39:01", + "Valentina Black~I'm a Tree (Extended Mix)~02.08.2024 20:47:08", + "Nebula~Clairvoyant Dreams~02.08.2024 20:53:20", + "Kiko & Dave Davis feat. Phoebe~Living in Space (Dub Mix)~02.08.2024 20:58:11", + "Lilly Palmer~Before Acid~02.08.2024 21:03:53", + "Sofi Tukker~Drinkee (Vintage Culture & John Summit Extended Mix)~02.08.2024 21:09:52", + "CID & Truth x Lies~Caroline (Extended Mix)~02.08.2024 21:14:09", + "Moby~Why Does My Heart Feel So Bad? (Oxia Remix)~02.08.2024 21:17:15", + "Ammo Avenue~Little Gurl (Extended Mix)~02.08.2024 21:22:46", + "James Hurr & Smokin Jo & Stealth~Beggin' For Change~02.08.2024 21:28:37", + "Kristine Blond~Love Shy (Sam Divine & CASSIMM Extended Remix)~02.08.2024 21:30:47", + "Vanilla Ace~Work On You (Original Mix)~02.08.2024 21:36:28", + "Truth X Lies~Like This~02.08.2024 21:42:05", + "Terri-Anne~Round Round~02.08.2024 21:44:07", + "Joanna Magik~Maneater~02.08.2024 21:46:32", + "Jen Payne & Kevin McKay~Feed Your Soul~02.08.2024 21:48:45", + "Kevin McKay & Eppers & Notelle~On My Own~02.08.2024 21:51:37", + "Nader Razdar & Kevin McKay~Get Ur Freak On (Kevin McKay Extended Mix)~02.08.2024 21:53:49", + "Philip Z~Yala (Extended Mix)~02.08.2024 21:59:40", + "Kyle Kinch & Kevin McKay~Hella~02.08.2024 22:05:53", + "Roze Wild~B-O-D-Y~02.08.2024 22:08:26", + "Jey Kurmis~Snoop~02.08.2024 22:11:09", + "Bootie Brown & Tame Impala & Gorillaz~New Gold (Dom Dolla Remix Extended)~02.08.2024 22:16:23", + "Eli Brown & Love Regenerator~Don't You Want Me (Original Mix)~02.08.2024 22:21:23", + "Local Singles~Voices~02.08.2024 22:25:59" + }; + + var traceChangeManager = new TraceChangeManager(TestHelper.CreateLogger()); + var sessionStateContainer = new SessionStateContainer(traceChangeManager); + var localStorageOptionsProviderMock = new Mock(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = @"(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,'*-?:]{1,})~(?'Track.Title'[a-zA-Z0-9_ .();äöü&'*-?:Ü]{1,})~(?'Track.StartDateTime'.{1,})" + } + }; + localStorageOptionsProviderMock.Setup(x => x.GetOptions()).ReturnsAsync(importOptions); + var textImportService = new TextImportService(); + var importManager = new ImportManager(sessionStateContainer, localStorageOptionsProviderMock.Object, textImportService, traceChangeManager); + var testHelper = new TestHelper(); + // Act + await importManager.ImportTextAsync(fileContent); + // Assert + Assert.IsNull(sessionStateContainer.Importfile?.AnalyseException); + Assert.IsNotNull(sessionStateContainer.ImportCuesheet); + Assert.AreEqual(30, sessionStateContainer.ImportCuesheet.Tracks.Count); + Assert.AreEqual("Innellea", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("The Golden Fort", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(TimeSpan.Zero, sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Begin); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).End); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(0).Length); + Assert.AreEqual("Nora En Pure", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Artist); + Assert.AreEqual("Diving with Whales (Daniel Portman Remix)", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Title); + Assert.AreEqual(new TimeSpan(0, 4, 33), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Begin); + Assert.AreEqual(new TimeSpan(0, 9, 54), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).End); + Assert.AreEqual(new TimeSpan(0, 5, 21), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(1).Length); + Assert.AreEqual("Local Singles", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Artist); + Assert.AreEqual("Voices", sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Title); + Assert.AreEqual(new TimeSpan(2, 15, 11), sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Begin); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).End); + Assert.IsNull(sessionStateContainer.ImportCuesheet.Tracks.ElementAt(29).Length); + } + } +} \ No newline at end of file diff --git a/AudioCuesheetEditorTests/Services/IO/TextImportServiceTests.cs b/AudioCuesheetEditorTests/Services/IO/TextImportServiceTests.cs new file mode 100644 index 00000000..305c580e --- /dev/null +++ b/AudioCuesheetEditorTests/Services/IO/TextImportServiceTests.cs @@ -0,0 +1,512 @@ +//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 +//. +using AudioCuesheetEditor.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.IO.Import; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditorTests.Properties; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace AudioCuesheetEditor.Services.IO.Tests +{ + [TestClass()] + public class TextImportServiceTests + { + [TestMethod()] + public void Analyse_SampleCuesheet_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); + Assert.AreEqual("c:\\tmp\\Testfile.mp3", importfile.AnalysedCuesheet.Audiofile); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); + } + + [TestMethod()] + public void Analyse_InvalidSchemeTracks_CreatesAnalyseException() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})", + SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNotNull(importfile.AnalyseException); + } + + [TestMethod()] + public void Analyse_InputfileWithExtraSeperator_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'.{1,})", + SchemeTracks = @"(?'Track.Position'\d{1,})[|](?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); + Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", importfile.AnalysedCuesheet.CDTextfile); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual((uint)6, importfile.AnalysedCuesheet.Tracks.ElementAt(5).Position); + Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); + } + + [TestMethod()] + public void Analyse_InvalidScheme_CreatesAnalyseException() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt A83412346734", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54", + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cuesheet.Cataloguenumber'.{1,})", + SchemeTracks = @"(?'Track.Position'.{1,})|(?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNotNull(importfile.AnalyseException); + } + + [TestMethod()] + public void Analyse_CuesheetWithTextfileAndCatalogueNumber_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist|CuesheetTitle c:\\tmp\\TestTextFile.cdt A83412346734", + "1|Sample Artist 1 - Sample Title 1 00:05:00", + "2|Sample Artist 2 - Sample Title 2 00:09:23", + "3|Sample Artist 3 - Sample Title 3 00:15:54", + "4|Sample Artist 4 - Sample Title 4 00:20:13", + "5|Sample Artist 5 - Sample Title 5 00:24:54", + "6|Sample Artist 6 - Sample Title 6 00:31:54", + "7|Sample Artist 7 - Sample Title 7 00:45:54", + "8|Sample Artist 8 - Sample Title 8 01:15:54", + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty, + String.Empty + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = @"(?'Cuesheet.Artist'\A.*)[|](?'Cuesheet.Title'\w{1,})\t{1,}(?'Cuesheet.CDTextfile'[a-zA-Z0-9_ .();äöü&:,\\]{1,})\t{1,}(?'Cuesheet.Cataloguenumber'.{1,})", + SchemeTracks = @"(?'Track.Position'.{1,})[|](?'Track.Artist'.{1,}) - (?'Track.Title'[a-zA-Z0-9_ ]{1,})\t{1,}(?'Track.End'.{1,})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); + Assert.AreEqual("c:\\tmp\\TestTextFile.cdt", importfile.AnalysedCuesheet.CDTextfile); + Assert.AreEqual("A83412346734", importfile.AnalysedCuesheet.Cataloguenumber); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual((uint)6, importfile.AnalysedCuesheet.Tracks.ElementAt(5).Position); + Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); + } + + [TestMethod()] + public void Analyse_CuesheeTracksOnly_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54", + "Sample Artist 9 - Sample Title 9" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual(9, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(new TimeSpan(1, 15, 54), importfile.AnalysedCuesheet.Tracks.ElementAt(7).End); + } + + [TestMethod()] + public void Analyse_CuesheetBug213_CreatesValidCuesheet() + { + // Arrange + var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug_213); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var importService = new TextImportService(); + var importOptions = new ImportOptions() + { + TimeSpanFormat = new TimeSpanFormat() { Scheme = "(?'TimeSpanFormat.Minutes'\\d{1,})[:](?'TimeSpanFormat.Seconds'\\d{1,})" }, + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual(4, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(2, 3, 23), importfile.AnalysedCuesheet.Tracks.ElementAt(3).End); + } + + [TestMethod()] + public void Analyse_CuesheetWithFlags_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "Sample Artist 1 - Sample Title 1 00:05:00 DCP", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54 PRE, DCP", + "Sample Artist 4 - Sample Title 4 00:20:13 4CH", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54 PRE DCP 4CH", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54 PRE DCP 4CH SCMS" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = "(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Track.Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'Track.End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.Flags'[a-zA-Z 0-9,]{1,})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(0).Flags.Contains(Flag.DCP)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(2).Flags.Contains(Flag.DCP)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(2).Flags.Contains(Flag.PRE)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(3).Flags.Contains(Flag.FourCH)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(5).Flags.Contains(Flag.FourCH)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(5).Flags.Contains(Flag.PRE)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(5).Flags.Contains(Flag.DCP)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(7).Flags.Contains(Flag.DCP)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(7).Flags.Contains(Flag.PRE)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(7).Flags.Contains(Flag.FourCH)); + Assert.IsTrue(importfile.AnalysedCuesheet.Tracks.ElementAt(7).Flags.Contains(Flag.SCMS)); + } + + [TestMethod()] + public void Analyse_CuesheetWithPreGapAndPostGap_CreatesValidCuesheet() + { + // Arrange + var fileContent = new List + { + "Sample Artist 1 - Sample Title 1 00:00:02 00:05:00 00:00:00", + "Sample Artist 2 - Sample Title 2 00:00:04 00:09:23 00:00:00", + "Sample Artist 3 - Sample Title 3 00:00:00 00:15:54 00:00:02", + "Sample Artist 4 - Sample Title 4 00:00:00 00:20:13 00:00:03", + "Sample Artist 5 - Sample Title 5 00:00:00 00:24:54 00:00:04", + "Sample Artist 6 - Sample Title 6 00:00:00 00:31:54 00:00:01", + "Sample Artist 7 - Sample Title 7 00:00:00 00:45:54 00:00:00", + "Sample Artist 8 - Sample Title 8 00:00:02 01:15:54 00:00:00" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = "(?'Track.Artist'[a-zA-Z0-9_ .();äöü&:,]{1,}) - (?'Track.Title'[a-zA-Z0-9_ .();äöü]{1,})\t{1,}(?'Track.PreGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.End'[0-9]{2}[:][0-9]{2}[:][0-9]{2})\t{1,}(?'Track.PostGap'[0-9]{2}[:][0-9]{2}[:][0-9]{2})" + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual(new TimeSpan(0, 0, 2), importfile.AnalysedCuesheet.Tracks.ElementAt(0).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 4), importfile.AnalysedCuesheet.Tracks.ElementAt(1).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(1).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(2).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 2), importfile.AnalysedCuesheet.Tracks.ElementAt(2).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(3).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 3), importfile.AnalysedCuesheet.Tracks.ElementAt(3).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(4).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 4), importfile.AnalysedCuesheet.Tracks.ElementAt(4).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(5).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 1), importfile.AnalysedCuesheet.Tracks.ElementAt(5).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(6).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(6).PostGap); + Assert.AreEqual(new TimeSpan(0, 0, 2), importfile.AnalysedCuesheet.Tracks.ElementAt(7).PreGap); + Assert.AreEqual(new TimeSpan(0, 0, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(7).PostGap); + } + + [TestMethod()] + public void Analyse_SchemeCuesheetOnly_CreatesFileContentRecognizedOnlyForCuesheet() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = null + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.IsNotNull(importfile.FileContentRecognized); + Assert.AreEqual(String.Format("{0} - {1} {2}", + String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetArtist"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "CuesheetTitle"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "c:\\tmp\\Testfile.mp3")), importfile.FileContentRecognized.First()); + Assert.AreEqual("CuesheetArtist", importfile.AnalysedCuesheet.Artist); + Assert.AreEqual("CuesheetTitle", importfile.AnalysedCuesheet.Title); + Assert.AreEqual("c:\\tmp\\Testfile.mp3", importfile.AnalysedCuesheet.Audiofile); + Assert.AreEqual(0, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual("Sample Artist 1 - Sample Title 1 00:05:00", importfile.FileContentRecognized.ElementAt(1)); + Assert.AreEqual("Sample Artist 2 - Sample Title 2 00:09:23", importfile.FileContentRecognized.ElementAt(2)); + Assert.AreEqual("Sample Artist 3 - Sample Title 3 00:15:54", importfile.FileContentRecognized.ElementAt(3)); + Assert.AreEqual("Sample Artist 4 - Sample Title 4 00:20:13", importfile.FileContentRecognized.ElementAt(4)); + Assert.AreEqual("Sample Artist 5 - Sample Title 5 00:24:54", importfile.FileContentRecognized.ElementAt(5)); + Assert.AreEqual("Sample Artist 6 - Sample Title 6 00:31:54", importfile.FileContentRecognized.ElementAt(6)); + Assert.AreEqual("Sample Artist 7 - Sample Title 7 00:45:54", importfile.FileContentRecognized.ElementAt(7)); + Assert.AreEqual("Sample Artist 8 - Sample Title 8 01:15:54", importfile.FileContentRecognized.ElementAt(8)); + } + + [TestMethod()] + public void Analyse_CuesheetWithoutTracks_CreatesValidFileContentRecognized() + { + // Arrange + var fileContent = new List + { + "CuesheetArtist - CuesheetTitle c:\\tmp\\Testfile.mp3", + "Sample Artist 1 - Sample Title 1 00:05:00", + "Sample Artist 2 - Sample Title 2 00:09:23", + "Sample Artist 3 - Sample Title 3 00:15:54", + "Sample Artist 4 - Sample Title 4 00:20:13", + "Sample Artist 5 - Sample Title 5 00:24:54", + "Sample Artist 6 - Sample Title 6 00:31:54", + "Sample Artist 7 - Sample Title 7 00:45:54", + "Sample Artist 8 - Sample Title 8 01:15:54" + }; + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = TextImportScheme.DefaultSchemeCuesheet, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.IsNotNull(importfile.FileContentRecognized); + Assert.AreEqual(8, importfile.AnalysedCuesheet.Tracks.Count); + Assert.AreEqual("Sample Artist 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Artist); + Assert.AreEqual("Sample Title 1", importfile.AnalysedCuesheet.Tracks.ElementAt(0).Title); + Assert.AreEqual(new TimeSpan(0, 5, 0), importfile.AnalysedCuesheet.Tracks.ElementAt(0).End); + Assert.AreEqual(String.Format("{0} - {1} {2}", + String.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Artist 8"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "Sample Title 8"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "01:15:54")), importfile.FileContentRecognized.Last()); + } + + [TestMethod()] + public void Analyse_TextfileBug233_CreatesValidFileContentRecognized() + { + // Arrange + var textImportMemoryStream = new MemoryStream(Resources.Textimport_Bug__233); + using var reader = new StreamReader(textImportMemoryStream); + List lines = []; + while (reader.EndOfStream == false) + { + lines.Add(reader.ReadLine()); + } + var fileContent = lines.AsReadOnly(); + var importService = new TextImportService(); + var importOptions = new ImportOptions + { + TextImportScheme = new TextImportScheme() + { + SchemeCuesheet = null, + SchemeTracks = TextImportScheme.DefaultSchemeTracks + } + }; + // Act + var importfile = importService.Analyse(importOptions, fileContent); + // Assert + Assert.IsNull(importfile.AnalyseException); + Assert.IsNotNull(importfile.AnalysedCuesheet); + Assert.IsNotNull(importfile.FileContentRecognized); + Assert.AreEqual(String.Format("{0} - {1}\t\t\t\t\t\t\t\t{2}", + String.Format(CuesheetConstants.RecognizedMarkHTML, "Age Of Love"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "The Age Of Love (Charlotte De Witte & Enrico Sangiuliano Remix)"), + String.Format(CuesheetConstants.RecognizedMarkHTML, "04:29:28")), importfile.FileContentRecognized.ElementAt(53)); + } + } +} \ No newline at end of file