diff --git a/Documentation/NamingTemplates.md b/Documentation/NamingTemplates.md
index 184570ea..b364fa04 100644
--- a/Documentation/NamingTemplates.md
+++ b/Documentation/NamingTemplates.md
@@ -105,13 +105,13 @@ As an example, this folder template will place all Liberated podcasts into a "Po
## Series Formatters
|Formatter|Description|Example Usage|Example Result|
|-|-|-|-|
-|\{N \| # \| ID\}|Formats the series using the series part tags. \{N\} = Series Name \{#\} = Number order in series \{ID\} = Audible Series ID
Default is \{N\}|``````|Sherlock HolmesSherlock HolmesSherlock Holmes, 1, B08376S3R2|
+|\{N \| # \| ID\}|Formats the series using the series part tags. \{N\} = Series Name \{#\} = Number order in series \{#:[Number_Formatter](#number-formatters)\} = Number order in series, formatted \{ID\} = Audible Series ID
Default is \{N\}|````````|Sherlock HolmesSherlock HolmesSherlock Holmes, 1-6, B08376S3R2Sherlock Holmes, B08376S3R2, 01.0-06.0|
## Series List Formatters
|Formatter|Description|Example Usage|Example Result|
|-|-|-|-|
|separator()|Speficy the text used to join multiple series names.
Default is ", "|``|Sherlock Holmes; Some Other Series|
-|format(\{N \| # \| ID\})|Formats the series properties using the name series tags. See [Series Formatter Usage](#series-formatters) above.|``separator(; )]>```|Sherlock Holmes, 1; Some Other Series, 1herlock Holmes, B08376S3R2; Some Other Series, B000000000|
+|format(\{N \| # \| ID\})|Formats the series properties using the name series tags. See [Series Formatter Usage](#series-formatters) above.|``separator(; )]>```|Sherlock Holmes, 1-6; Book Collection, 1B08376S3R2-Sherlock Holmes, 01.0-06.0, B000000000-Book Collection, 01.0|
|max(#)|Only use the first # of series
Default is all series|``|Sherlock Holmes|
## Name Formatters
diff --git a/Source/FileLiberator/DownloadOptions.cs b/Source/FileLiberator/DownloadOptions.cs
index 0b761f6a..578c8ff5 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 string? SeriesNumber => LibraryBookDto.FirstSeries?.Number;
+ public string? SeriesNumber => LibraryBookDto.FirstSeries?.Order?.ToString();
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/LibationFileManager/Templates/SeriesDto.cs b/Source/LibationFileManager/Templates/SeriesDto.cs
index cfd3d3fd..a2d7a867 100644
--- a/Source/LibationFileManager/Templates/SeriesDto.cs
+++ b/Source/LibationFileManager/Templates/SeriesDto.cs
@@ -1,27 +1,34 @@
using System;
+using System.Text.RegularExpressions;
#nullable enable
namespace LibationFileManager.Templates;
-public record SeriesDto : IFormattable
+public partial record SeriesDto : IFormattable
{
public string Name { get; }
- public string? Number { get; }
+ public SeriesOrder Order { get; }
public string AudibleSeriesId { get; }
public SeriesDto(string name, string? number, string audibleSeriesId)
{
Name = name;
- Number = number;
+ Order = SeriesOrder.Parse(number);
AudibleSeriesId = audibleSeriesId;
}
public override string ToString() => Name.Trim();
public string ToString(string? format, IFormatProvider? _)
=> string.IsNullOrWhiteSpace(format) ? ToString()
- : format
- .Replace("{N}", Name)
- .Replace("{#}", Number?.ToString())
- .Replace("{ID}", AudibleSeriesId)
- .Trim();
+ : FormatRegex().Replace(format, MatchEvaluator)
+ .Replace("{N}", Name)
+ .Replace("{ID}", AudibleSeriesId)
+ .Trim();
+
+ private string MatchEvaluator(Match match)
+ => Order?.ToString(match.Groups[1].Value, null) ?? "";
+
+ /// Format must have at least one of the string {N}, {#}, {ID}
+ [GeneratedRegex(@"{#(?:\:(.*?))?}")]
+ public static partial Regex FormatRegex();
}
diff --git a/Source/LibationFileManager/Templates/SeriesListFormat.cs b/Source/LibationFileManager/Templates/SeriesListFormat.cs
index 1127eaa5..9db3e441 100644
--- a/Source/LibationFileManager/Templates/SeriesListFormat.cs
+++ b/Source/LibationFileManager/Templates/SeriesListFormat.cs
@@ -12,6 +12,6 @@ internal partial class SeriesListFormat : IListFormat
: IListFormat.Join(formatString, series);
/// Format must have at least one of the string {N}, {#}, {ID}
- [GeneratedRegex(@"[Ff]ormat\((.*?(?:{[N#]}|{ID})+.*?)\)")]
+ [GeneratedRegex(@"[Ff]ormat\((.*?(?:{#(?:\:.*?)?}|{N}|{ID})+.*?)\)")]
public static partial Regex FormatRegex();
}
diff --git a/Source/LibationFileManager/Templates/SeriesOrder.cs b/Source/LibationFileManager/Templates/SeriesOrder.cs
new file mode 100644
index 00000000..ddf31a63
--- /dev/null
+++ b/Source/LibationFileManager/Templates/SeriesOrder.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+#nullable enable
+namespace LibationFileManager.Templates;
+
+public class SeriesOrder : IFormattable
+{
+ public object[] OrderParts { get; }
+ private SeriesOrder(object[] orderParts)
+ {
+ OrderParts = orderParts;
+ }
+
+ public override string ToString() => ToString(null, null);
+
+ ///
+ /// Use float formatters to format the number parts of the order.
+ ///
+ public string ToString(string? format, IFormatProvider? formatProvider)
+ => string.Concat(OrderParts.Select(p => p is float f ? f.ToString(format) : p.ToString())).Trim();
+
+ public static SeriesOrder Parse(string? order)
+ {
+ List