From 663f70b8bfe3de148c1f1f7cc465dfa28541745d Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 29 Jul 2025 12:18:37 -0600 Subject: [PATCH 01/12] Use series Order string instead of parsed float in template tags (#1056) --- Source/AaxDecrypter/AaxcDownloadConvertBase.cs | 4 ++-- Source/AaxDecrypter/IDownloadOptions.cs | 2 +- Source/FileLiberator/DownloadOptions.cs | 2 +- Source/FileLiberator/UtilityExtensions.cs | 2 +- .../LibationFileManager/Templates/SeriesDto.cs | 4 ++-- .../Templates/TemplateEditor[T].cs | 2 +- .../LibationFileManager.Tests/TemplatesTests.cs | 16 +++++++++------- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs index 2e00afc4..18161443 100644 --- a/Source/AaxDecrypter/AaxcDownloadConvertBase.cs +++ b/Source/AaxDecrypter/AaxcDownloadConvertBase.cs @@ -126,8 +126,8 @@ namespace AaxDecrypter if (DownloadOptions.SeriesName is string series) AaxFile.AppleTags.AppleListBox.EditOrAddFreeformTag(tagDomain, "SERIES", series); - if (DownloadOptions.SeriesNumber is float part) - AaxFile.AppleTags.AppleListBox.EditOrAddFreeformTag(tagDomain, "PART", part.ToString()); + if (DownloadOptions.SeriesNumber is string part) + AaxFile.AppleTags.AppleListBox.EditOrAddFreeformTag(tagDomain, "PART", part); } OnRetrievedTitle(AaxFile.AppleTags.TitleSansUnabridged); diff --git a/Source/AaxDecrypter/IDownloadOptions.cs b/Source/AaxDecrypter/IDownloadOptions.cs index d80a5b95..11e79db8 100644 --- a/Source/AaxDecrypter/IDownloadOptions.cs +++ b/Source/AaxDecrypter/IDownloadOptions.cs @@ -43,7 +43,7 @@ namespace AaxDecrypter string? Publisher { get; } string? Language { get; } string? SeriesName { get; } - float? SeriesNumber { get; } + string? SeriesNumber { get; } NAudio.Lame.LameConfig? LameConfig { get; } bool Downsample { get; } bool MatchSourceBitrate { get; } diff --git a/Source/FileLiberator/DownloadOptions.cs b/Source/FileLiberator/DownloadOptions.cs index 29e7b286..a276e6ad 100644 --- a/Source/FileLiberator/DownloadOptions.cs +++ b/Source/FileLiberator/DownloadOptions.cs @@ -26,7 +26,7 @@ namespace FileLiberator public string Language => LibraryBook.Book.Language; public string? AudibleProductId => LibraryBookDto.AudibleProductId; public string? SeriesName => LibraryBookDto.FirstSeries?.Name; - public float? SeriesNumber => LibraryBookDto.FirstSeries?.Number; + public string? SeriesNumber => LibraryBookDto.FirstSeries?.Number; public NAudio.Lame.LameConfig? LameConfig { get; } public string UserAgent => AudibleApi.Resources.Download_User_Agent; public bool StripUnabridged => Config.AllowLibationFixup && Config.StripUnabridged; diff --git a/Source/FileLiberator/UtilityExtensions.cs b/Source/FileLiberator/UtilityExtensions.cs index b0e05cf7..73ac5bbc 100644 --- a/Source/FileLiberator/UtilityExtensions.cs +++ b/Source/FileLiberator/UtilityExtensions.cs @@ -83,7 +83,7 @@ namespace FileLiberator .Select(sb => new SeriesDto( sb.Series.Name, - sb.Book.IsEpisodeParent() ? null : sb.Index, + sb.Book.IsEpisodeParent() ? null : sb.Order, sb.Series.AudibleSeriesId) ).ToList(); } diff --git a/Source/LibationFileManager/Templates/SeriesDto.cs b/Source/LibationFileManager/Templates/SeriesDto.cs index 834c8db4..cfd3d3fd 100644 --- a/Source/LibationFileManager/Templates/SeriesDto.cs +++ b/Source/LibationFileManager/Templates/SeriesDto.cs @@ -7,9 +7,9 @@ public record SeriesDto : IFormattable { public string Name { get; } - public float? Number { get; } + public string? Number { get; } public string AudibleSeriesId { get; } - public SeriesDto(string name, float? number, string audibleSeriesId) + public SeriesDto(string name, string? number, string audibleSeriesId) { Name = name; Number = number; diff --git a/Source/LibationFileManager/Templates/TemplateEditor[T].cs b/Source/LibationFileManager/Templates/TemplateEditor[T].cs index bc0daf2e..edd060c2 100644 --- a/Source/LibationFileManager/Templates/TemplateEditor[T].cs +++ b/Source/LibationFileManager/Templates/TemplateEditor[T].cs @@ -68,7 +68,7 @@ namespace LibationFileManager.Templates YearPublished = 2017, Authors = [new("Arthur Conan Doyle", "B000AQ43GQ"), new("Stephen Fry - introductions", "B000APAGVS")], Narrators = [new("Stephen Fry", null)], - Series = [new("Sherlock Holmes", 1, "B08376S3R2"), new("Some Other Series", 1, "B000000000")], + Series = [new("Sherlock Holmes", "1-6", "B08376S3R2"), new("Book Collection", "1", "B000000000")], Codec = "AAC-LC", LibationVersion = Configuration.LibationVersion?.ToVersionString(), FileVersion = "36217811", diff --git a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs index 4f92c27d..fbc9eb96 100644 --- a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs +++ b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs @@ -24,7 +24,7 @@ namespace TemplatesTests public static class Shared { public static LibraryBookDto GetLibraryBook() - => GetLibraryBook([new SeriesDto("Sherlock Holmes", 1, "B08376S3R2")]); + => GetLibraryBook([new SeriesDto("Sherlock Holmes", "1", "B08376S3R2")]); public static LibraryBookDto GetLibraryBook(IEnumerable series) => new() @@ -367,12 +367,13 @@ namespace TemplatesTests [TestMethod] - [DataRow("", "Series A, Series B, Series C")] - [DataRow("", "Series A, Series B, Series C")] + [DataRow("", "Series A, Series B, Series C, Series D")] + [DataRow("", "Series A, Series B, Series C, Series D")] [DataRow("", "Series A")] [DataRow("", "Series A, Series B")] [DataRow("", "Series A, Series B, Series C")] - [DataRow("", "Series A, 1, B1; Series B, 6, B2; Series C, 2, B3")] + [DataRow("", "Series A, Series B, Series C, Series D")] + [DataRow("", "Series A, 1, B1; Series B, 6, B2; Series C, 2, B3; Series D, 1-5, B4")] [DataRow("", "Series A, 1, B1; Series B, 6, B2; Series C, 2, B3")] [DataRow("", "Series A, 1, B1; Series B, 6, B2")] [DataRow("", "Series A")] @@ -383,9 +384,10 @@ namespace TemplatesTests var bookDto = GetLibraryBook(); bookDto.Series = [ - new("Series A", 1, "B1"), - new("Series B", 6, "B2"), - new("Series C", 2, "B3") + new("Series A", "1", "B1"), + new("Series B", "6", "B2"), + new("Series C", "2", "B3"), + new("Series D", "1-5", "B4"), ]; Templates.TryGetTemplate(template, out var fileTemplate).Should().BeTrue(); From 7024bbf82307a4c47e053276d496c90c4b381e16 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 29 Jul 2025 15:20:52 -0600 Subject: [PATCH 02/12] Provide NTFS default characters for non-windows users (#1258) --- Source/FileManager/LogArchiver.cs | 2 +- Source/FileManager/ReplacementCharacters.cs | 103 ++++++++---------- .../Dialogs/EditReplacementChars.axaml | 41 ++++--- .../Dialogs/EditReplacementChars.axaml.cs | 16 +-- .../Views/MainWindow.axaml.cs | 2 +- .../Configuration.PersistentSettings.cs | 2 +- .../Dialogs/EditReplacementChars.cs | 6 +- Source/LibationWinForms/Form1._NonUI.cs | 2 +- .../FileManager.Tests/FileUtilityTests.cs | 14 +-- .../TemplatesTests.cs | 6 +- 10 files changed, 98 insertions(+), 96 deletions(-) diff --git a/Source/FileManager/LogArchiver.cs b/Source/FileManager/LogArchiver.cs index 04342220..e0041da5 100644 --- a/Source/FileManager/LogArchiver.cs +++ b/Source/FileManager/LogArchiver.cs @@ -56,7 +56,7 @@ namespace FileManager { ArgumentValidator.EnsureNotNull(name, nameof(name)); - name = ReplacementCharacters.Barebones.ReplaceFilenameChars(name); + name = ReplacementCharacters.Barebones(true).ReplaceFilenameChars(name); return Task.Run(() => AddFileInternal(name, contents.Span, comment)); } diff --git a/Source/FileManager/ReplacementCharacters.cs b/Source/FileManager/ReplacementCharacters.cs index 3cf55dd8..ed2422da 100644 --- a/Source/FileManager/ReplacementCharacters.cs +++ b/Source/FileManager/ReplacementCharacters.cs @@ -74,12 +74,14 @@ namespace FileManager } public override int GetHashCode() => Replacements.GetHashCode(); - public static readonly ReplacementCharacters Default - = IsWindows - ? new() - { - Replacements = new Replacement[] - { + public static ReplacementCharacters Default(bool ntfs) => ntfs ? HiFi_NTFS : HiFi_Other; + public static ReplacementCharacters LoFiDefault(bool ntfs) => ntfs ? LoFi_NTFS : LoFi_Other; + public static ReplacementCharacters Barebones(bool ntfs) => ntfs ? BareBones_NTFS : BareBones_Other; + + #region Defaults + private static readonly ReplacementCharacters HiFi_NTFS = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("∕"), Replacement.FilenameBackSlash(""), @@ -91,28 +93,23 @@ namespace FileManager Replacement.Colon("_"), Replacement.Asterisk("✱"), Replacement.QuestionMark("?"), - Replacement.Pipe("⏐"), - } - } - : new() - { - Replacements = new Replacement[] - { + Replacement.Pipe("⏐")] + }; + + private static readonly ReplacementCharacters HiFi_Other = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("∕"), Replacement.FilenameBackSlash("\\"), Replacement.OpenQuote("“"), Replacement.CloseQuote("”"), - Replacement.OtherQuote("\"") - } - }; + Replacement.OtherQuote("\"")] + }; - public static readonly ReplacementCharacters LoFiDefault - = IsWindows - ? new() - { - Replacements = new Replacement[] - { + private static readonly ReplacementCharacters LoFi_NTFS = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("_"), Replacement.FilenameBackSlash("_"), @@ -121,50 +118,44 @@ namespace FileManager Replacement.OtherQuote("'"), Replacement.OpenAngleBracket("{"), Replacement.CloseAngleBracket("}"), - Replacement.Colon("-"), - } - } - : new () - { - Replacements = new Replacement[] - { + Replacement.Colon("-")] + }; + + private static readonly ReplacementCharacters LoFi_Other = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("_"), Replacement.FilenameBackSlash("\\"), Replacement.OpenQuote("\""), Replacement.CloseQuote("\""), - Replacement.OtherQuote("\"") - } - }; + Replacement.OtherQuote("\"")] + }; - public static readonly ReplacementCharacters Barebones - = IsWindows - ? new () - { - Replacements = new Replacement[] - { + private static readonly ReplacementCharacters BareBones_NTFS = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("_"), Replacement.FilenameBackSlash("_"), Replacement.OpenQuote("_"), Replacement.CloseQuote("_"), - Replacement.OtherQuote("_") - } - } - : new () - { - Replacements = new Replacement[] - { + Replacement.OtherQuote("_")] + }; + + private static readonly ReplacementCharacters BareBones_Other = new() + { + Replacements = [ Replacement.OtherInvalid("_"), Replacement.FilenameForwardSlash("_"), Replacement.FilenameBackSlash("\\"), Replacement.OpenQuote("\""), Replacement.CloseQuote("\""), - Replacement.OtherQuote("\"") - } - }; + Replacement.OtherQuote("\"")] + }; + #endregion - private static bool IsWindows => Environment.OSVersion.Platform is PlatformID.Win32NT; + internal static bool IsWindows => Environment.OSVersion.Platform is PlatformID.Win32NT; private static readonly char[] invalidPathChars = Path.GetInvalidFileNameChars().Except(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar @@ -301,23 +292,21 @@ namespace FileManager public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { + var defaults = ReplacementCharacters.Default(ReplacementCharacters.IsWindows).Replacements; + var jObj = JObject.Load(reader); var replaceArr = jObj[nameof(Replacement)]; - var dict - = replaceArr?.ToObject()?.ToList() - ?? ReplacementCharacters.Default.Replacements; - + var dict = replaceArr?.ToObject()?.ToList() ?? defaults; //Ensure that the first 6 replacements are for the expected chars and that all replacement strings are valid. //If not, reset to default. - for (int i = 0; i < Replacement.FIXED_COUNT; i++) { if (dict.Count < Replacement.FIXED_COUNT - || dict[i].CharacterToReplace != ReplacementCharacters.Barebones.Replacements[i].CharacterToReplace - || dict[i].Description != ReplacementCharacters.Barebones.Replacements[i].Description) + || dict[i].CharacterToReplace != defaults[i].CharacterToReplace + || dict[i].Description != defaults[i].Description) { - dict = ReplacementCharacters.Default.Replacements; + dict = defaults; break; } diff --git a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml index d04f1161..f60f2f16 100644 --- a/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml +++ b/Source/LibationAvalonia/Dialogs/EditReplacementChars.axaml @@ -6,6 +6,8 @@ MinWidth="500" MinHeight="450" Width="500" Height="450" x:Class="LibationAvalonia.Dialogs.EditReplacementChars" + xmlns:dialogs="clr-namespace:LibationAvalonia.Dialogs" + x:DataType="dialogs:EditReplacementChars" Title="Illegal Character Replacement"> + ItemsSource="{CompiledBinding replacements}"> - - - + + - - + + - - + + @@ -55,21 +56,31 @@ - - -