mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-03-30 04:45:05 -04:00
Format details on fields specified in format. Introduce by colon.
Use dictionaries for field access and sorting. Added sorting to series lists.
This commit is contained in:
@@ -12,18 +12,65 @@ public static partial class CommonFormatters
|
||||
public delegate string PropertyFormatter<in T>(ITemplateTag templateTag, T? value, string formatString, CultureInfo? culture);
|
||||
|
||||
public static string StringFormatter(ITemplateTag _, string? value, string formatString, CultureInfo? culture)
|
||||
=> _StringFormatter(value, formatString, culture);
|
||||
|
||||
private static string _StringFormatter(string? value, string formatString, CultureInfo? culture)
|
||||
{
|
||||
if (value is null) return "";
|
||||
if (string.IsNullOrEmpty(value)) return string.Empty;
|
||||
if (string.IsNullOrEmpty(formatString)) return value;
|
||||
|
||||
var match = StringFormatRegex().Match(formatString);
|
||||
if (!match.Success) return value;
|
||||
|
||||
// first shorten the string if a number is specified in the format string
|
||||
if (int.TryParse(match.Groups["left"].ValueSpan, out var length) && length < value.Length)
|
||||
value = value[..length];
|
||||
|
||||
culture ??= CultureInfo.CurrentCulture;
|
||||
|
||||
return formatString switch
|
||||
return match.Groups["case"].ValueSpan switch
|
||||
{
|
||||
"u" or "U" => value.ToUpper(culture),
|
||||
"l" or "L" => value.ToLower(culture),
|
||||
"T" => culture.TextInfo.ToTitleCase(value),
|
||||
"t" => culture.TextInfo.ToTitleCase(value.ToLower(culture)),
|
||||
_ => value,
|
||||
};
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^\s*(?<left>\d+)?\s*(?<case>[uUlLtT])?\s*$")]
|
||||
private static partial Regex StringFormatRegex();
|
||||
|
||||
public static string TemplateStringFormatter<T>(T toFormat, string? templateString, IFormatProvider? provider, Dictionary<string, Func<T, object?>> replacements)
|
||||
{
|
||||
if (string.IsNullOrEmpty(templateString)) return "";
|
||||
|
||||
// is this function is called from toString implementation of the IFormattable interface, we only get a IFormatProvider
|
||||
var culture = provider as CultureInfo ?? (provider?.GetFormat(typeof(CultureInfo)) as CultureInfo);
|
||||
return TagFormatRegex().Replace(templateString, GetValueForMatchingTag);
|
||||
|
||||
string GetValueForMatchingTag(Match m)
|
||||
{
|
||||
var tag = m.Groups["tag"].Value;
|
||||
if (!replacements.TryGetValue(tag, out var getter)) return m.Value;
|
||||
|
||||
var value = getter(toFormat);
|
||||
var format = m.Groups["format"].Value;
|
||||
return value switch
|
||||
{
|
||||
IFormattable formattable => formattable.ToString(format, provider),
|
||||
_ => _StringFormatter(value?.ToString(), format, culture),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// The templateString is scanned for contained braces with an enclosed tagname.
|
||||
// The tagname may be followed by an optional format specifier separated by a colon.
|
||||
// All other parts of the template string are left untouched as well as the braces where the tagname is unknown.
|
||||
// TemplateStringFormatter will use a dictionary to lookup the tagname and the corresponding value getter.
|
||||
[GeneratedRegex(@"\{(?<tag>[[A-Z]+|#)(?::(?<format>.*?))?\}", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex TagFormatRegex();
|
||||
|
||||
public static string FormattableFormatter(ITemplateTag _, IFormattable? value, string formatString, CultureInfo? culture)
|
||||
=> value?.ToString(formatString, culture) ?? "";
|
||||
|
||||
@@ -52,12 +99,6 @@ public static partial class CommonFormatters
|
||||
|
||||
public static string LanguageShortFormatter(ITemplateTag templateTag, string? language, string formatString, CultureInfo? culture)
|
||||
{
|
||||
if (language is null)
|
||||
return "";
|
||||
|
||||
language = language.Trim();
|
||||
if (language.Length > 3) language = language[..3];
|
||||
|
||||
return StringFormatter(templateTag, language, formatString, culture);
|
||||
return StringFormatter(templateTag, language?.Trim(), "3u", culture);
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class NamingTemplate
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="propertyClasses">Instances of the TClass used in <see cref="PropertyTagCollection{TClass}"/> and <see cref="ConditionalTagCollection{TClass}"/></param>
|
||||
public TemplatePart Evaluate(params object?[] propertyClasses) //CultureInfo? culture,
|
||||
public TemplatePart Evaluate(params object?[] propertyClasses)
|
||||
{
|
||||
if (_templateToString is null)
|
||||
throw new InvalidOperationException();
|
||||
@@ -39,8 +39,7 @@ public class NamingTemplate
|
||||
// Match propertyClasses to the arguments required by templateToString.DynamicInvoke().
|
||||
// First parameter is "this", so ignore it.
|
||||
var parameters = _templateToString.Method.GetParameters();
|
||||
int skip = _templateToString.Target == null ? 0 : 1;
|
||||
var delegateArgTypes = parameters.Skip(skip).ToList();
|
||||
var delegateArgTypes = parameters.Skip(1).ToList();
|
||||
|
||||
object?[] args = new object?[delegateArgTypes.Count];
|
||||
// args = delegateArgTypes.Join(propertyClasses, dat => dat.ParameterType, pc => pc?.GetType(), (_, i) => i,
|
||||
@@ -48,14 +47,7 @@ public class NamingTemplate
|
||||
for (int i = 0; i < delegateArgTypes.Count; i++)
|
||||
{
|
||||
var p = delegateArgTypes[i];
|
||||
if (typeof(CultureInfo).IsAssignableFrom(p.ParameterType) && false)
|
||||
{
|
||||
args[i] = null;//culture;
|
||||
}
|
||||
else
|
||||
{
|
||||
args[i] = propertyClasses.FirstOrDefault(pc => pc != null && p.ParameterType.IsInstanceOfType(pc));
|
||||
}
|
||||
args[i] = propertyClasses.FirstOrDefault(pc => pc != null && p.ParameterType.IsInstanceOfType(pc));
|
||||
}
|
||||
|
||||
if (args.Length != delegateArgTypes.Count)
|
||||
|
||||
@@ -1,13 +1,28 @@
|
||||
using NameParser;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FileManager.NamingTemplate;
|
||||
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
public class ContributorDto(string name, string? audibleContributorId) : IFormattable
|
||||
{
|
||||
public HumanName HumanName { get; } = new(RemoveSuffix(name), Prefer.FirstOverPrefix);
|
||||
private HumanName HumanName { get; } = new(RemoveSuffix(name), Prefer.FirstOverPrefix);
|
||||
private string? AudibleContributorId { get; } = audibleContributorId;
|
||||
|
||||
public static readonly Dictionary<string, Func<ContributorDto, object?>> FormatReplacements = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
// Single-word names parse as first names. Use it as last name.
|
||||
{ "L", dto => string.IsNullOrWhiteSpace(dto.HumanName.Last) ? dto.HumanName.First : dto.HumanName.Last },
|
||||
// Because of the above, if we have only a first name, then we'd double the name as "FirstName FirstName", so clear the first name in that situation.
|
||||
{ "F", dto => string.IsNullOrWhiteSpace(dto.HumanName.Last) ? dto.HumanName.Last : dto.HumanName.First },
|
||||
|
||||
{ "T", dto => dto.HumanName.Title },
|
||||
{ "M", dto => dto.HumanName.Middle },
|
||||
{ "S", dto => dto.HumanName.Suffix },
|
||||
{ "ID", dto => dto.AudibleContributorId },
|
||||
};
|
||||
|
||||
public override string ToString()
|
||||
=> ToString("{T} {F} {M} {L} {S}", null);
|
||||
|
||||
@@ -16,19 +31,7 @@ public class ContributorDto(string name, string? audibleContributorId) : IFormat
|
||||
if (string.IsNullOrWhiteSpace(format))
|
||||
return ToString();
|
||||
|
||||
//Single-word names parse as first names. Use it as last name.
|
||||
var lastName = string.IsNullOrWhiteSpace(HumanName.Last) ? HumanName.First : HumanName.Last;
|
||||
//Because of the above, if the have only a first name, then we'd double the name as "FirstName FirstName", so clear the first name in that situation.
|
||||
var firstName = string.IsNullOrWhiteSpace(HumanName.Last) ? HumanName.Last : HumanName.First;
|
||||
|
||||
return format
|
||||
.Replace("{T}", HumanName.Title)
|
||||
.Replace("{F}", firstName)
|
||||
.Replace("{M}", HumanName.Middle)
|
||||
.Replace("{L}", lastName)
|
||||
.Replace("{S}", HumanName.Suffix)
|
||||
.Replace("{ID}", AudibleContributorId)
|
||||
.Trim();
|
||||
return CommonFormatters.TemplateStringFormatter(this, format, provider, FormatReplacements).Trim();
|
||||
}
|
||||
|
||||
private static string RemoveSuffix(string namesString)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using FileManager.NamingTemplate;
|
||||
using System;
|
||||
using FileManager.NamingTemplate;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -11,24 +12,39 @@ internal partial class NameListFormat : IListFormat<NameListFormat>
|
||||
public static string Formatter(ITemplateTag _, IEnumerable<ContributorDto>? names, string formatString, CultureInfo? culture)
|
||||
=> names is null
|
||||
? string.Empty
|
||||
: IListFormat<NameListFormat>.Join(formatString, Sort(names, formatString), culture);
|
||||
: IListFormat<NameListFormat>.Join(formatString, Sort(names, formatString, ContributorDto.FormatReplacements), culture);
|
||||
|
||||
private static IEnumerable<ContributorDto> Sort(IEnumerable<ContributorDto> names, string formatString)
|
||||
private static IEnumerable<T> Sort<T>(IEnumerable<T> entries, string formatString, Dictionary<string, Func<T, object?>> formatReplacements)
|
||||
{
|
||||
var sortMatch = SortRegex().Match(formatString);
|
||||
return
|
||||
sortMatch.Success
|
||||
? sortMatch.Groups[1].Value == "F" ? names.OrderBy(n => n.HumanName.First)
|
||||
: sortMatch.Groups[1].Value == "M" ? names.OrderBy(n => n.HumanName.Middle)
|
||||
: sortMatch.Groups[1].Value == "L" ? names.OrderBy(n => n.HumanName.Last)
|
||||
: names
|
||||
: names;
|
||||
if (!sortMatch.Success) return entries;
|
||||
|
||||
IOrderedEnumerable<T>? ordered = null;
|
||||
foreach (Match m in SortTokenizer().Matches(sortMatch.Groups["pattern"].Value))
|
||||
{
|
||||
// Dictionary is case-insensitive, no ToUpper needed
|
||||
if (!formatReplacements.TryGetValue(m.Groups["token"].Value, out var selector))
|
||||
continue;
|
||||
|
||||
ordered = ordered is null
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
? entries.OrderBy(selector)
|
||||
: ordered.ThenBy(selector);
|
||||
}
|
||||
|
||||
return ordered ?? entries;
|
||||
}
|
||||
|
||||
/// <summary> Sort must have exactly one of the characters F, M, or L </summary>
|
||||
[GeneratedRegex(@"[Ss]ort\(\s*?([FML])\s*?\)")]
|
||||
private const string Token = @"(?:[TFMLS]|ID)";
|
||||
|
||||
/// <summary> Sort must have at least one of the token labels T, F, M, L, S or ID.Add multiple tokens to sort by multiple fields. Spaces may be used to separate tokens.</summary>
|
||||
[GeneratedRegex($@"[Ss]ort\(\s*(?i:(?<pattern>(?:{Token}\s*?)+))\s*\)")]
|
||||
private static partial Regex SortRegex();
|
||||
|
||||
[GeneratedRegex($@"\G(?<token>{Token})\s*", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex SortTokenizer();
|
||||
|
||||
/// <summary> Format must have at least one of the string {T}, {F}, {M}, {L}, {S}, or {ID} </summary>
|
||||
[GeneratedRegex(@"[Ff]ormat\((.*?(?:{[TFMLS]}|{ID})+.*?)\)")]
|
||||
[GeneratedRegex($@"[Ff]ormat\((.*?\{{{Token}(?::.*?)?\}}.*?)\)")]
|
||||
public static partial Regex FormatRegex();
|
||||
}
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Collections.Generic;
|
||||
using FileManager.NamingTemplate;
|
||||
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
public partial record SeriesDto(string? Name, string? Number, string AudibleSeriesId) : IFormattable
|
||||
public record SeriesDto(string? Name, string? Number, string AudibleSeriesId) : IFormattable
|
||||
{
|
||||
public SeriesOrder Order { get; } = SeriesOrder.Parse(Number);
|
||||
|
||||
public static readonly Dictionary<string, Func<SeriesDto, object?>> FormatReplacements = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ "#", dto => dto.Order },
|
||||
{ "N", dto => dto.Name },
|
||||
{ "ID", dto => dto.AudibleSeriesId }
|
||||
};
|
||||
|
||||
public override string? ToString() => Name?.Trim();
|
||||
|
||||
public string ToString(string? format, IFormatProvider? provider)
|
||||
=> string.IsNullOrWhiteSpace(format) ? ToString() ?? string.Empty
|
||||
: FormatRegex().Replace(format, MatchEvaluator)
|
||||
.Replace("{N}", Name)
|
||||
.Replace("{ID}", AudibleSeriesId)
|
||||
.Trim();
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(format))
|
||||
return ToString() ?? string.Empty;
|
||||
|
||||
private string MatchEvaluator(Match match)
|
||||
=> Order?.ToString(match.Groups[1].Value, null) ?? "";
|
||||
|
||||
/// <summary> Format must have at least one of the string {N}, {#}, {ID} </summary>
|
||||
[GeneratedRegex(@"{#(?:\:(.*?))?}")]
|
||||
private static partial Regex FormatRegex();
|
||||
return CommonFormatters.TemplateStringFormatter(this, format, provider, FormatReplacements).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using FileManager.NamingTemplate;
|
||||
using System;
|
||||
using FileManager.NamingTemplate;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace LibationFileManager.Templates;
|
||||
@@ -10,9 +12,39 @@ internal partial class SeriesListFormat : IListFormat<SeriesListFormat>
|
||||
public static string Formatter(ITemplateTag _, IEnumerable<SeriesDto>? series, string formatString, CultureInfo? culture)
|
||||
=> series is null
|
||||
? string.Empty
|
||||
: IListFormat<SeriesListFormat>.Join(formatString, series, culture);
|
||||
: IListFormat<SeriesListFormat>.Join(formatString, Sort(series, formatString, SeriesDto.FormatReplacements), culture);
|
||||
|
||||
private static IEnumerable<T> Sort<T>(IEnumerable<T> entries, string formatString, Dictionary<string, Func<T, object?>> formatReplacements)
|
||||
{
|
||||
var sortMatch = SortRegex().Match(formatString);
|
||||
if (!sortMatch.Success) return entries;
|
||||
|
||||
IOrderedEnumerable<T>? ordered = null;
|
||||
foreach (Match m in SortTokenizer().Matches(sortMatch.Groups["pattern"].Value))
|
||||
{
|
||||
// Dictionary is case-insensitive, no ToUpper needed
|
||||
if (!formatReplacements.TryGetValue(m.Groups["token"].Value, out var selector))
|
||||
continue;
|
||||
|
||||
ordered = ordered is null
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
? entries.OrderBy(selector)
|
||||
: ordered.ThenBy(selector);
|
||||
}
|
||||
|
||||
return ordered ?? entries;
|
||||
}
|
||||
|
||||
private const string Token = @"(?:[#N]|ID)";
|
||||
|
||||
/// <summary> Sort must have at least one of the token labels T, F, M, L, S or ID. Use lower case for descending direction and add multiple tokens to sort by multiple fields. Spaces may be used to separate tokens.</summary>
|
||||
[GeneratedRegex($@"[Ss]ort\(\s*(?i:(?<pattern>(?:{Token}\s*?)+))\s*\)")]
|
||||
private static partial Regex SortRegex();
|
||||
|
||||
[GeneratedRegex($@"\G(?<token>{Token})\s*", RegexOptions.IgnoreCase)]
|
||||
private static partial Regex SortTokenizer();
|
||||
|
||||
/// <summary> Format must have at least one of the string {N}, {#}, {ID} </summary>
|
||||
[GeneratedRegex(@"[Ff]ormat\((.*?(?:{#(?:\:.*?)?}|{N}|{ID})+.*?)\)")]
|
||||
[GeneratedRegex($@"[Ff]ormat\((.*?\{{{Token}(?::.*?)?\}}.*?)\)")]
|
||||
public static partial Regex FormatRegex();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class SeriesOrder : IFormattable
|
||||
=> string.Concat(OrderParts.Select(p => p switch
|
||||
{
|
||||
float f => f.ToString(format, formatProvider ?? CultureInfo.InvariantCulture),
|
||||
IFormattable f => f.ToString(format, formatProvider),
|
||||
_ => p.ToString(),
|
||||
})).Trim();
|
||||
|
||||
|
||||
@@ -160,10 +160,12 @@ namespace TemplatesTests
|
||||
[TestMethod]
|
||||
[DataRow("<bitrate>Kbps <samplerate>Hz", "128Kbps 44100Hz")]
|
||||
[DataRow("<bitrate>Kbps <samplerate[6]>Hz", "128Kbps 044100Hz")]
|
||||
[DataRow("<bitrate[4]>Kbps <samplerate>Hz", "0128Kbps 44100Hz")]
|
||||
[DataRow("<bitrate[4]>Kbps <titleshort[u]>", "0128Kbps A STUDY IN SCARLET")]
|
||||
[DataRow("<bitrate[1]>Kbps <samplerate>Hz", "128Kbps 44100Hz")]
|
||||
[DataRow("<bitrate[2]>Kbps <titleshort[u]>", "128Kbps A STUDY IN SCARLET")]
|
||||
[DataRow("<bitrate[3]>Kbps <titleshort[t]>", "128Kbps A Study In Scarlet")]
|
||||
[DataRow("<bitrate[4]>Kbps <titleshort[l]>", "0128Kbps a study in scarlet")]
|
||||
[DataRow("<bitrate[4]>Kbps <samplerate[6]>Hz", "0128Kbps 044100Hz")]
|
||||
[DataRow("<codec[t]> <samplerate[6]>Hz", "Aac-Lc 044100Hz")]
|
||||
[DataRow("<codec[3T]> <titleshort[ 5 U ]>", "AAC A STU")]
|
||||
[DataRow("<bitrate [ 4 ] >Kbps <samplerate [ 6 ] >Hz", "0128Kbps 044100Hz")]
|
||||
public void FormatTags(string template, string expected)
|
||||
{
|
||||
@@ -333,11 +335,11 @@ namespace TemplatesTests
|
||||
[DataRow("<author>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery, Jon Bon Jovi, Paul Van Doren")]
|
||||
[DataRow("<author[]>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery, Jon Bon Jovi, Paul Van Doren")]
|
||||
[DataRow("<author[sort(F)]>", "Charles E. Gannon, Christopher John Fetherolf, Jill Conner Browne, Jon Bon Jovi, Lucy Maud Montgomery, Paul Van Doren")]
|
||||
[DataRow("<author[sort(L)]>", "Jon Bon Jovi, Jill Conner Browne, Christopher John Fetherolf, Charles E. Gannon, Lucy Maud Montgomery, Paul Van Doren")]
|
||||
[DataRow("<author[sort(M)]>", "Jon Bon Jovi, Paul Van Doren, Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery")]
|
||||
[DataRow("<author[sort(f)]>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery, Jon Bon Jovi, Paul Van Doren")]
|
||||
[DataRow("<author[sort(m)]>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery, Jon Bon Jovi, Paul Van Doren")]
|
||||
[DataRow("<author[sort(l)]>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery, Jon Bon Jovi, Paul Van Doren")]
|
||||
[DataRow("<author[sort(L)]>", "Jon Bon Jovi, Jill Conner Browne, Christopher John Fetherolf, Charles E. Gannon, Lucy Maud Montgomery, Paul Van Doren")]
|
||||
[DataRow("<author[sort(f)]>", "Charles E. Gannon, Christopher John Fetherolf, Jill Conner Browne, Jon Bon Jovi, Lucy Maud Montgomery, Paul Van Doren")]
|
||||
[DataRow("<author[sort(m)]>", "Jon Bon Jovi, Paul Van Doren, Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf, Lucy Maud Montgomery")]
|
||||
[DataRow("<author[sort(l)]>", "Jon Bon Jovi, Jill Conner Browne, Christopher John Fetherolf, Charles E. Gannon, Lucy Maud Montgomery, Paul Van Doren")]
|
||||
[DataRow("<author [ max( 1 ) ]>", "Jill Conner Browne")]
|
||||
[DataRow("<author[max(2)]>", "Jill Conner Browne, Charles E. Gannon")]
|
||||
[DataRow("<author[max(3)]>", "Jill Conner Browne, Charles E. Gannon, Christopher John Fetherolf")]
|
||||
|
||||
@@ -102,10 +102,16 @@ And this example will customize the title based on whether the book has a subtit
|
||||
|
||||
### Text Formatters
|
||||
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| --------- | -------------------------- | ------------------ | ------------------------------------------- |
|
||||
| L | Converts text to lowercase | \<title[L]\> | a study in scarlet꞉ a sherlock holmes novel |
|
||||
| U | Converts text to uppercase | \<title short[U]\> | A STUDY IN SCARLET |
|
||||
Text formatting can change length and case of the text. Use <#>, <#><case> or <case> to specify one or both of these.
|
||||
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| --------- | --------------------------------------------------------------- | ------------------ | ------------------------------------------- |
|
||||
| # | Cuts down the text to the specified number of characters | \<title[14]\> | A Study in Scar |
|
||||
| L | Converts text to lowercase | \<title[L]\> | a study in scarlet꞉ a sherlock holmes novel |
|
||||
| U | Converts text to uppercase | \<title short[U]\> | A STUDY IN SCARLET |
|
||||
| t | Converts text to title case | \<title[t]\> | The Abc Murders |
|
||||
| T | Converts text to title case where uppercase words are preserved | \<title[T]\> | The ABC Murders |
|
||||
| | | \<title[6T]\> | The AB |
|
||||
|
||||
### Series Formatters
|
||||
|
||||
@@ -115,25 +121,26 @@ And this example will customize the title based on whether the book has a subtit
|
||||
|
||||
### Series List Formatters
|
||||
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| separator() | Speficy the text used to join<br>multiple series names.<br><br>Default is ", " | `<series[separator(; )]>` | Sherlock Holmes; Some Other Series |
|
||||
| format(\{N \| # \| ID\}) | Formats the series properties<br>using the name series tags.<br>See [Series Formatter Usage](#series-formatters) above. | `<series[format({N}, {#})`<br>`separator(; )]>`<hr>`<series[format({ID}-{N}, {#:00.0})]>` | Sherlock Holmes, 1-6; Book Collection, 1<hr>B08376S3R2-Sherlock Holmes, 01.0-06.0, B000000000-Book Collection, 01.0 |
|
||||
| max(#) | Only use the first # of series<br><br>Default is all series | `<series[max(1)]>` | Sherlock Holmes |
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
|
||||
| separator() | Speficy the text used to join<br>multiple series names.<br><br>Default is ", " | `<series[separator(; )]>` | Sherlock Holmes; Some Other Series |
|
||||
| format(\{N \| # \| ID\}) | Formats the series properties<br>using the name series tags.<br>See [Series Formatter Usage](#series-formatters) above. | `<series[format({N}, {#})`<br>`separator(; )]>`<hr>`<series[format({ID}-{N}, {#:00.0})]>` | Sherlock Holmes, 1-6; Book Collection, 1<hr>B08376S3R2-Sherlock Holmes, 01.0-06.0, B000000000-Book Collection, 01.0 |
|
||||
| sort(\{N \| # \| ID\}) | Sorts the series by name, number or ID.<br><br>These terms define the primary, secondary, tertiary, … sorting order.<br>You may combine multiple terms in sequence to specify multi‑level sorting.<br><br>Default is unsorted | `<series[sort(N)`<br>`separator(; )]>` | Book Collection, 1; Sherlock Holmes, 1-6 |
|
||||
| max(#) | Only use the first # of series<br><br>Default is all series | `<series[max(1)]>` | Sherlock Holmes |
|
||||
|
||||
### Name Formatters
|
||||
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| \{T \| F \| M \| L \| S \| ID\} | Formats the human name using<br>the name part tags.<br>\{T\} = Title (e.g. "Dr.")<br>\{F\} = First name<br>\{M\} = Middle name<br>\{L\} = Last Name<br>\{S\} = Suffix (e.g. "PhD")<br>\{ID\} = Audible Contributor ID<br><br>Default is \{T\} \{F\} \{M\} \{L\} \{S\} | `<first narrator[{L}, {F}]>`<hr>`<first author[{L}, {F} _{ID}_]>` | Fry, Stephen<hr>Doyle, Arthur \_B000AQ43GQ\_;<br>Fry, Stephen \_B000APAGVS\_ |
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------- |
|
||||
| \{T \| F \| M \| L \| S \| ID\} | Formats the human name using<br>the name part tags.<br>\{T:[Text_Formatter](#text-formatters)\} = Title (e.g. "Dr.")<br>\{F:[Text_Formatter](#Text-Formatters)\} = First name<br>\{M:[Text_Formatter](#text-formatters)\} = Middle name<br>\{L:[Text_Formatter](#text-formatters)\} = Last Name<br>\{S:[Text_Formatter](#text-formatters)\} = Suffix (e.g. "PhD")<br>\{ID:[Text_Formatter](#text-formatters)\} = Audible Contributor ID<br><br>Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the correspoing formatter.<br><br>Default is \{T\} \{F\} \{M\} \{L\} \{S\} | `<first narrator[{L}, {F:1}.]>`<hr>`<first author[{L:u}, {F} _{ID}_]>` | Fry, S.<hr>DOYLE, Arthur \_B000AQ43GQ\_ |
|
||||
|
||||
### Name List Formatters
|
||||
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| separator() | Speficy the text used to join<br>multiple people's names.<br><br>Default is ", " | `<author[separator(; )]>` | Arthur Conan Doyle; Stephen Fry |
|
||||
| format(\{T \| F \| M \| L \| S \| ID\}) | Formats the human name using<br>the name part tags.<br>See [Name Formatter Usage](#name-formatters) above. | `<author[format({L}, {F})`<br>`separator(; )]>`<hr>`<author[format({L}, {F}`<br>`_{ID}_) separator(; )]>` | Doyle, Arthur; Fry, Stephen<hr>Doyle, Arthur \_B000AQ43GQ\_;<br>Fry, Stephen \_B000APAGVS\_ |
|
||||
| sort(F \| M \| L) | Sorts the names by first, middle,<br>or last name<br><br>Default is unsorted | `<author[sort(M)]>` | Stephen Fry, Arthur Conan Doyle |
|
||||
| Formatter | Description | Example Usage | Example Result |
|
||||
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| separator() | Speficy the text used to join<br>multiple people's names.<br><br>Default is ", " | `<author[separator(; )]>` | Arthur Conan Doyle; Stephen Fry |
|
||||
| format(\{T \| F \| M \| L \| S \| ID\}) | Formats the human name using<br>the name part tags.<br>See [Name Formatter Usage](#name-formatters) above. | `<author[format({L:u}, {F})`<br>`separator(; )]>`<hr>`<author[format({L}, {F:1}.`<br>`_{ID}_) separator(; )]>` | DOYLE, Arthur; FRY, Stephen<hr>Doyle, A. \_B000AQ43GQ\_;<br>Fry, S. \_B000APAGVS\_ |
|
||||
| sort(T \| F \| M \| L \| S \| ID) | Sorts the names by title,<br> first, middle, or last name,<br>suffix or Audible Contributor ID<br><br>These terms define the primary, secondary, tertiary, … sorting order.<br>You may combine multiple terms in sequence to specify multi‑level sorting.<br><br>Default is unsorted | `<author[sort(M)]>`<hr>`<author[sort(Fl)]>`<hr><author[sort(L FM ID)]> | Stephen Fry, Arthur Conan Doyle<hr>Stephen Fry,Stephen King<hr>John P. Smith \_B000TTTBBB\_, John P. Smith \_B000TTTCCC\_, John S. Smith \_B000HHHVVV\_ |
|
||||
| max(#) | Only use the first # of names<br><br>Default is all names | `<author[max(1)]>` | Arthur Conan Doyle |
|
||||
|
||||
### Number Formatters
|
||||
|
||||
Reference in New Issue
Block a user