Increase tag template options for contributor and series types
- Add template tag support for multiple series - Add series ID and contributor ID to template tags - <first author> and <first narrator> are now name types with name formatter support - Properly import contributor IDs into database - Updated docs
This commit is contained in:
parent
0a9e489f48
commit
7d806e0f3e
31 changed files with 425 additions and 255 deletions
|
|
@ -306,41 +306,41 @@ namespace LibationFileManager
|
|||
[Description("How to format the folders in which files will be saved")]
|
||||
public string FolderTemplate
|
||||
{
|
||||
get => getTemplate<Templates.FolderTemplate>();
|
||||
set => setTemplate<Templates.FolderTemplate>(value);
|
||||
get => getTemplate<Templates.Templates.FolderTemplate>();
|
||||
set => setTemplate<Templates.Templates.FolderTemplate>(value);
|
||||
}
|
||||
|
||||
[Description("How to format the saved pdf and audio files")]
|
||||
public string FileTemplate
|
||||
{
|
||||
get => getTemplate<Templates.FileTemplate>();
|
||||
set => setTemplate<Templates.FileTemplate>(value);
|
||||
get => getTemplate<Templates.Templates.FileTemplate>();
|
||||
set => setTemplate<Templates.Templates.FileTemplate>(value);
|
||||
}
|
||||
|
||||
[Description("How to format the saved audio files when split by chapters")]
|
||||
public string ChapterFileTemplate
|
||||
{
|
||||
get => getTemplate<Templates.ChapterFileTemplate>();
|
||||
set => setTemplate<Templates.ChapterFileTemplate>(value);
|
||||
get => getTemplate<Templates.Templates.ChapterFileTemplate>();
|
||||
set => setTemplate<Templates.Templates.ChapterFileTemplate>(value);
|
||||
}
|
||||
|
||||
[Description("How to format the file's Title stored in metadata")]
|
||||
public string ChapterTitleTemplate
|
||||
{
|
||||
get => getTemplate<Templates.ChapterTitleTemplate>();
|
||||
set => setTemplate<Templates.ChapterTitleTemplate>(value);
|
||||
get => getTemplate<Templates.Templates.ChapterTitleTemplate>();
|
||||
set => setTemplate<Templates.Templates.ChapterTitleTemplate>(value);
|
||||
}
|
||||
|
||||
private string getTemplate<T>([CallerMemberName] string propertyName = "")
|
||||
where T : Templates, ITemplate, new()
|
||||
where T : Templates.Templates, Templates.ITemplate, new()
|
||||
{
|
||||
return Templates.GetTemplate<T>(GetString(defaultValue: T.DefaultTemplate, propertyName)).TemplateText;
|
||||
return Templates.Templates.GetTemplate<T>(GetString(defaultValue: T.DefaultTemplate, propertyName)).TemplateText;
|
||||
}
|
||||
|
||||
private void setTemplate<T>(string newValue, [CallerMemberName] string propertyName = "")
|
||||
where T : Templates, ITemplate, new()
|
||||
where T : Templates.Templates, Templates.ITemplate, new()
|
||||
{
|
||||
SetString(Templates.GetTemplate<T>(newValue).TemplateText, propertyName);
|
||||
SetString(Templates.Templates.GetTemplate<T>(newValue).TemplateText, propertyName);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
{
|
||||
public class BookDto
|
||||
{
|
||||
public string? AudibleProductId { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
public string? TitleWithSubtitle { get; set; }
|
||||
public string? Locale { get; set; }
|
||||
public int? YearPublished { get; set; }
|
||||
|
||||
public IEnumerable<string>? Authors { get; set; }
|
||||
public string? AuthorNames => Authors is null ? null : string.Join(", ", Authors);
|
||||
public string? FirstAuthor => Authors?.FirstOrDefault();
|
||||
|
||||
public IEnumerable<string>? Narrators { get; set; }
|
||||
public string? NarratorNames => Narrators is null? null: string.Join(", ", Narrators);
|
||||
public string? FirstNarrator => Narrators?.FirstOrDefault();
|
||||
|
||||
public string? SeriesName { get; set; }
|
||||
public float? SeriesNumber { get; set; }
|
||||
public bool IsSeries => !string.IsNullOrEmpty(SeriesName);
|
||||
public bool IsPodcastParent { get; set; }
|
||||
public bool IsPodcast { get; set; }
|
||||
|
||||
public int BitRate { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
public int Channels { get; set; }
|
||||
public DateTime FileDate { get; set; } = DateTime.Now;
|
||||
public DateTime? DatePublished { get; set; }
|
||||
public string? Language { get; set; }
|
||||
}
|
||||
|
||||
public class LibraryBookDto : BookDto
|
||||
{
|
||||
public DateTime? DateAdded { get; set; }
|
||||
public string? Account { get; set; }
|
||||
public string? AccountNickname { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
using FileManager.NamingTemplate;
|
||||
using NameParser;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
{
|
||||
internal partial class NameListFormat
|
||||
{
|
||||
public static string Formatter(ITemplateTag _, IEnumerable<string>? names, string formatString)
|
||||
{
|
||||
if (names is null) return "";
|
||||
|
||||
var humanNames = names.Select(n => new HumanName(RemoveSuffix(n), Prefer.FirstOverPrefix));
|
||||
|
||||
var sortedNames = Sort(humanNames, formatString);
|
||||
var nameFormatString = Format(formatString, defaultValue: "{T} {F} {M} {L} {S}");
|
||||
var separatorString = Separator(formatString, defaultValue: ", ");
|
||||
var maxNames = Max(formatString, defaultValue: humanNames.Count());
|
||||
|
||||
var formattedNames = string.Join(separatorString, sortedNames.Take(maxNames).Select(n => FormatName(n, nameFormatString)));
|
||||
|
||||
while (formattedNames.Contains(" "))
|
||||
formattedNames = formattedNames.Replace(" ", " ");
|
||||
|
||||
return formattedNames;
|
||||
}
|
||||
|
||||
private static string RemoveSuffix(string namesString)
|
||||
{
|
||||
namesString = namesString.Replace('’', '\'').Replace(" - Ret.", ", Ret.");
|
||||
int dashIndex = namesString.IndexOf(" - ");
|
||||
return (dashIndex > 0 ? namesString[..dashIndex] : namesString).Trim();
|
||||
}
|
||||
|
||||
private static IEnumerable<HumanName> Sort(IEnumerable<HumanName> humanNames, string formatString)
|
||||
{
|
||||
var sortMatch = SortRegex().Match(formatString);
|
||||
return
|
||||
sortMatch.Success
|
||||
? sortMatch.Groups[1].Value == "F" ? humanNames.OrderBy(n => n.First)
|
||||
: sortMatch.Groups[1].Value == "M" ? humanNames.OrderBy(n => n.Middle)
|
||||
: sortMatch.Groups[1].Value == "L" ? humanNames.OrderBy(n => n.Last)
|
||||
: humanNames
|
||||
: humanNames;
|
||||
}
|
||||
|
||||
private static string Format(string formatString, string defaultValue)
|
||||
{
|
||||
var formatMatch = FormatRegex().Match(formatString);
|
||||
return formatMatch.Success ? formatMatch.Groups[1].Value : defaultValue;
|
||||
}
|
||||
|
||||
private static string Separator(string formatString, string defaultValue)
|
||||
{
|
||||
var separatorMatch = SeparatorRegex().Match(formatString);
|
||||
return separatorMatch.Success ? separatorMatch.Groups[1].Value : defaultValue;
|
||||
}
|
||||
|
||||
private static int Max(string formatString, int defaultValue)
|
||||
{
|
||||
var maxMatch = MaxRegex().Match(formatString);
|
||||
return maxMatch.Success && int.TryParse(maxMatch.Groups[1].Value, out var max) ? int.Max(1, max) : defaultValue;
|
||||
}
|
||||
|
||||
private static string FormatName(HumanName humanName, string nameFormatString)
|
||||
{
|
||||
//Single-word names parse as first names. Use it as last name.
|
||||
var lastName = string.IsNullOrWhiteSpace(humanName.Last) ? humanName.First : humanName.Last;
|
||||
|
||||
nameFormatString
|
||||
= nameFormatString
|
||||
.Replace("{T}", "{0}")
|
||||
.Replace("{F}", "{1}")
|
||||
.Replace("{M}", "{2}")
|
||||
.Replace("{L}", "{3}")
|
||||
.Replace("{S}", "{4}");
|
||||
|
||||
return string.Format(nameFormatString, humanName.Title, humanName.First, humanName.Middle, lastName, humanName.Suffix).Trim();
|
||||
}
|
||||
|
||||
/// <summary> Sort must have exactly one of the characters F, M, or L </summary>
|
||||
[GeneratedRegex(@"[Ss]ort\(\s*?([FML])\s*?\)")]
|
||||
private static partial Regex SortRegex();
|
||||
/// <summary> Format must have at least one of the string {T}, {F}, {M}, {L}, or {S} </summary>
|
||||
[GeneratedRegex(@"[Ff]ormat\((.*?(?:{[TFMLS]})+.*?)\)")]
|
||||
private static partial Regex FormatRegex();
|
||||
/// <summary> Separator can be anything </summary>
|
||||
[GeneratedRegex(@"[Ss]eparator\((.*?)\)")]
|
||||
private static partial Regex SeparatorRegex();
|
||||
/// <summary> Max must have a 1 or 2-digit number </summary>
|
||||
[GeneratedRegex(@"[Mm]ax\(\s*?(\d{1,2})\s*?\)")]
|
||||
private static partial Regex MaxRegex();
|
||||
}
|
||||
}
|
||||
44
Source/LibationFileManager/Templates/ContributorDto.cs
Normal file
44
Source/LibationFileManager/Templates/ContributorDto.cs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
using NameParser;
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
public class ContributorDto : IFormattable
|
||||
{
|
||||
public HumanName HumanName { get; }
|
||||
public string? AudibleContributorId { get; }
|
||||
public ContributorDto(string name, string? audibleContributorId)
|
||||
{
|
||||
HumanName = new HumanName(RemoveSuffix(name), Prefer.FirstOverPrefix);
|
||||
AudibleContributorId = audibleContributorId;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
=> ToString("{T} {F} {M} {L} {S}", null);
|
||||
|
||||
public string ToString(string? format, IFormatProvider? _)
|
||||
{
|
||||
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;
|
||||
|
||||
return format
|
||||
.Replace("{T}", HumanName.Title)
|
||||
.Replace("{F}", HumanName.First)
|
||||
.Replace("{M}", HumanName.Middle)
|
||||
.Replace("{L}", lastName)
|
||||
.Replace("{S}", HumanName.Suffix)
|
||||
.Replace("{ID}", AudibleContributorId)
|
||||
.Trim();
|
||||
}
|
||||
|
||||
private static string RemoveSuffix(string namesString)
|
||||
{
|
||||
namesString = namesString.Replace('’', '\'').Replace(" - Ret.", ", Ret.");
|
||||
int dashIndex = namesString.IndexOf(" - ");
|
||||
return (dashIndex > 0 ? namesString[..dashIndex] : namesString).Trim();
|
||||
}
|
||||
}
|
||||
53
Source/LibationFileManager/Templates/IListFormat[TList].cs
Normal file
53
Source/LibationFileManager/Templates/IListFormat[TList].cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
internal partial interface IListFormat<TList> where TList : IListFormat<TList>
|
||||
{
|
||||
static string Join<T>(string formatString, IEnumerable<T> items)
|
||||
where T : IFormattable
|
||||
{
|
||||
var itemFormatter = Formatter(formatString);
|
||||
var separatorString = Separator(formatString) ?? ", ";
|
||||
var maxValues = Max(formatString) ?? items.Count();
|
||||
|
||||
var formattedValues = string.Join(separatorString, items.Take(maxValues).Select(n => n.ToString(itemFormatter, null)));
|
||||
|
||||
while (formattedValues.Contains(" "))
|
||||
formattedValues = formattedValues.Replace(" ", " ");
|
||||
|
||||
return formattedValues;
|
||||
|
||||
static string? Formatter(string formatString)
|
||||
{
|
||||
var formatMatch = TList.FormatRegex().Match(formatString);
|
||||
return formatMatch.Success ? formatMatch.Groups[1].Value : null;
|
||||
}
|
||||
|
||||
static int? Max(string formatString)
|
||||
{
|
||||
var maxMatch = MaxRegex().Match(formatString);
|
||||
return maxMatch.Success && int.TryParse(maxMatch.Groups[1].Value, out var max) ? int.Max(1, max) : null;
|
||||
}
|
||||
|
||||
static string? Separator(string formatString)
|
||||
{
|
||||
var separatorMatch = SeparatorRegex().Match(formatString);
|
||||
return separatorMatch.Success ? separatorMatch.Groups[1].Value : ", ";
|
||||
}
|
||||
}
|
||||
|
||||
static abstract Regex FormatRegex();
|
||||
|
||||
/// <summary> Separator can be anything </summary>
|
||||
[GeneratedRegex(@"[Ss]eparator\((.*?)\)")]
|
||||
private static partial Regex SeparatorRegex();
|
||||
|
||||
/// <summary> Max must have a 1 or 2-digit number </summary>
|
||||
[GeneratedRegex(@"[Mm]ax\(\s*?(\d{1,2})\s*?\)")]
|
||||
private static partial Regex MaxRegex();
|
||||
}
|
||||
43
Source/LibationFileManager/Templates/LibraryBookDto.cs
Normal file
43
Source/LibationFileManager/Templates/LibraryBookDto.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
public class BookDto
|
||||
{
|
||||
public string? AudibleProductId { get; set; }
|
||||
public string? Title { get; set; }
|
||||
public string? Subtitle { get; set; }
|
||||
public string? TitleWithSubtitle { get; set; }
|
||||
public string? Locale { get; set; }
|
||||
public int? YearPublished { get; set; }
|
||||
|
||||
public IEnumerable<ContributorDto>? Authors { get; set; }
|
||||
public ContributorDto? FirstAuthor => Authors?.FirstOrDefault();
|
||||
|
||||
public IEnumerable<ContributorDto>? Narrators { get; set; }
|
||||
public ContributorDto? FirstNarrator => Narrators?.FirstOrDefault();
|
||||
|
||||
public IEnumerable<SeriesDto>? Series { get; set; }
|
||||
public SeriesDto? FirstSeries => Series?.FirstOrDefault();
|
||||
|
||||
public bool IsSeries => Series is not null;
|
||||
public bool IsPodcastParent { get; set; }
|
||||
public bool IsPodcast { get; set; }
|
||||
|
||||
public int BitRate { get; set; }
|
||||
public int SampleRate { get; set; }
|
||||
public int Channels { get; set; }
|
||||
public DateTime FileDate { get; set; } = DateTime.Now;
|
||||
public DateTime? DatePublished { get; set; }
|
||||
public string? Language { get; set; }
|
||||
}
|
||||
|
||||
public class LibraryBookDto : BookDto
|
||||
{
|
||||
public DateTime? DateAdded { get; set; }
|
||||
public string? Account { get; set; }
|
||||
public string? AccountNickname { get; set; }
|
||||
}
|
||||
33
Source/LibationFileManager/Templates/NameListFormat.cs
Normal file
33
Source/LibationFileManager/Templates/NameListFormat.cs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
using FileManager.NamingTemplate;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
internal partial class NameListFormat : IListFormat<NameListFormat>
|
||||
{
|
||||
public static string Formatter(ITemplateTag _, IEnumerable<ContributorDto>? names, string formatString)
|
||||
=> names is null ? string.Empty
|
||||
: IListFormat<NameListFormat>.Join(formatString, Sort(names, formatString));
|
||||
|
||||
private static IEnumerable<ContributorDto> Sort(IEnumerable<ContributorDto> names, string formatString)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary> Sort must have exactly one of the characters F, M, or L </summary>
|
||||
[GeneratedRegex(@"[Ss]ort\(\s*?([FML])\s*?\)")]
|
||||
private static partial Regex SortRegex();
|
||||
/// <summary> Format must have at least one of the string {T}, {F}, {M}, {L}, {S}, or {ID} </summary>
|
||||
[GeneratedRegex(@"[Ff]ormat\((.*?(?:{[TFMLS]}|{ID})+.*?)\)")]
|
||||
public static partial Regex FormatRegex();
|
||||
}
|
||||
27
Source/LibationFileManager/Templates/SeriesDto.cs
Normal file
27
Source/LibationFileManager/Templates/SeriesDto.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
public record SeriesDto : IFormattable
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public float? Number { get; }
|
||||
public string AudibleSeriesId { get; }
|
||||
public SeriesDto(string name, float? number, string audibleSeriesId)
|
||||
{
|
||||
Name = name;
|
||||
Number = 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();
|
||||
}
|
||||
17
Source/LibationFileManager/Templates/SeriesListFormat.cs
Normal file
17
Source/LibationFileManager/Templates/SeriesListFormat.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using FileManager.NamingTemplate;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager.Templates;
|
||||
|
||||
internal partial class SeriesListFormat : IListFormat<SeriesListFormat>
|
||||
{
|
||||
public static string Formatter(ITemplateTag _, IEnumerable<SeriesDto>? series, string formatString)
|
||||
=> series is null ? string.Empty
|
||||
: IListFormat<SeriesListFormat>.Join(formatString, series);
|
||||
|
||||
/// <summary> Format must have at least one of the string {N}, {#}, {ID} </summary>
|
||||
[GeneratedRegex(@"[Ff]ormat\((.*?(?:{[N#]}|{ID})+.*?)\)")]
|
||||
public static partial Regex FormatRegex();
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
using AaxDecrypter;
|
||||
using FileManager;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
namespace LibationFileManager.Templates
|
||||
{
|
||||
public interface ITemplateEditor
|
||||
{
|
||||
|
|
@ -61,16 +60,15 @@ namespace LibationFileManager
|
|||
AccountNickname = "my account",
|
||||
DateAdded = new DateTime(2022, 6, 9, 0, 0, 0),
|
||||
DatePublished = new DateTime(2017, 2, 27, 0, 0, 0),
|
||||
AudibleProductId = "123456789",
|
||||
AudibleProductId = "B06WLMWF2S",
|
||||
Title = "A Study in Scarlet",
|
||||
TitleWithSubtitle = "A Study in Scarlet: A Sherlock Holmes Novel",
|
||||
Subtitle = "A Sherlock Holmes Novel",
|
||||
Locale = "us",
|
||||
YearPublished = 2017,
|
||||
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
||||
Narrators = new List<string> { "Stephen Fry" },
|
||||
SeriesName = "Sherlock Holmes",
|
||||
SeriesNumber = 1,
|
||||
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")],
|
||||
BitRate = 128,
|
||||
SampleRate = 44100,
|
||||
Channels = 2,
|
||||
|
|
@ -131,7 +129,7 @@ namespace LibationFileManager
|
|||
|
||||
if (!templateEditor.IsFolder && !templateEditor.IsFilePath)
|
||||
throw new InvalidOperationException($"This method is only for File and Folder templates. Use {nameof(CreateNameEditor)} for name templates");
|
||||
|
||||
|
||||
if (templateEditor.IsFolder)
|
||||
templateEditor.File = Templates.File;
|
||||
else
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
using FileManager.NamingTemplate;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
namespace LibationFileManager.Templates
|
||||
{
|
||||
public sealed class TemplateTags : ITemplateTag
|
||||
{
|
||||
|
|
@ -33,15 +33,15 @@ namespace LibationFileManager
|
|||
public static TemplateTags FirstAuthor { get; } = new TemplateTags("first author", "First author");
|
||||
public static TemplateTags Narrator { get; } = new TemplateTags("narrator", "Narrator(s)");
|
||||
public static TemplateTags FirstNarrator { get; } = new TemplateTags("first narrator", "First narrator");
|
||||
public static TemplateTags Series { get; } = new TemplateTags("series", "Name of series");
|
||||
// can't also have a leading zeros version. Too many weird edge cases. Eg: "1-4"
|
||||
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series");
|
||||
public static TemplateTags Series { get; } = new TemplateTags("series", "All series to which the book belongs (if any)");
|
||||
public static TemplateTags FirstSeries { get; } = new TemplateTags("first series", "First series");
|
||||
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series (alias for <first series[{#}]>");
|
||||
public static TemplateTags Bitrate { get; } = new TemplateTags("bitrate", "File's orig. bitrate");
|
||||
public static TemplateTags SampleRate { get; } = new TemplateTags("samplerate", "File's orig. sample rate");
|
||||
public static TemplateTags Channels { get; } = new TemplateTags("channels", "Number of audio channels");
|
||||
public static TemplateTags Account { get; } = new TemplateTags("account", "Audible account of this book");
|
||||
public static TemplateTags AccountNickname { get; } = new TemplateTags("account nickname", "Audible account nickname of this book");
|
||||
public static TemplateTags Locale { get; } = new ("locale", "Region/country");
|
||||
public static TemplateTags Locale { get; } = new("locale", "Region/country");
|
||||
public static TemplateTags YearPublished { get; } = new("year", "Year published");
|
||||
public static TemplateTags Language { get; } = new("language", "Book's language");
|
||||
public static TemplateTags LanguageShort { get; } = new("language short", "Book's language abbreviated. Eg: ENG");
|
||||
|
|
@ -10,7 +10,7 @@ using FileManager.NamingTemplate;
|
|||
using NameParser;
|
||||
|
||||
#nullable enable
|
||||
namespace LibationFileManager
|
||||
namespace LibationFileManager.Templates
|
||||
{
|
||||
public interface ITemplate
|
||||
{
|
||||
|
|
@ -58,19 +58,19 @@ namespace LibationFileManager
|
|||
{
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.FolderTemplate))]
|
||||
(_,e) => _folder = GetTemplate<FolderTemplate>(e.NewValue as string);
|
||||
(_, e) => _folder = GetTemplate<FolderTemplate>(e.NewValue as string);
|
||||
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.FileTemplate))]
|
||||
(_, e) => _file = GetTemplate<FileTemplate>(e.NewValue as string);
|
||||
(_, e) => _file = GetTemplate<FileTemplate>(e.NewValue as string);
|
||||
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.ChapterFileTemplate))]
|
||||
(_, e) => _chapterFile = GetTemplate<ChapterFileTemplate>(e.NewValue as string);
|
||||
(_, e) => _chapterFile = GetTemplate<ChapterFileTemplate>(e.NewValue as string);
|
||||
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.ChapterTitleTemplate))]
|
||||
(_, e) => _chapterTitle = GetTemplate<ChapterTitleTemplate>(e.NewValue as string);
|
||||
(_, e) => _chapterTitle = GetTemplate<ChapterTitleTemplate>(e.NewValue as string);
|
||||
|
||||
HumanName.Suffixes.Add("ret");
|
||||
HumanName.Titles.Add("professor");
|
||||
|
|
@ -121,7 +121,7 @@ namespace LibationFileManager
|
|||
ArgumentValidator.EnsureNotNull(fileExtension, nameof(fileExtension));
|
||||
|
||||
replacements ??= Configuration.Instance.ReplacementCharacters;
|
||||
return GetFilename(baseDir, fileExtension,replacements, returnFirstExisting, libraryBookDto);
|
||||
return GetFilename(baseDir, fileExtension, replacements, returnFirstExisting, libraryBookDto);
|
||||
}
|
||||
|
||||
public LongPath GetFilename(LibraryBookDto libraryBookDto, MultiConvertFileProperties multiChapProps, string baseDir, string fileExtension, ReplacementCharacters? replacements = null, bool returnFirstExisting = false)
|
||||
|
|
@ -154,7 +154,7 @@ namespace LibationFileManager
|
|||
//If file already exists, GetValidFilename will append " (n)" to the filename.
|
||||
//This could cause the filename length to exceed MaxFilenameLength, so reduce
|
||||
//allowable filename length by 5 chars, allowing for up to 99 duplicates.
|
||||
var maxFilenameLength = LongPath.MaxFilenameLength -
|
||||
var maxFilenameLength = LongPath.MaxFilenameLength -
|
||||
(i < pathParts.Count - 1 || string.IsNullOrEmpty(fileExtension) ? 0 : fileExtension.Length + 5);
|
||||
|
||||
while (part.Sum(LongPath.GetFilesystemStringLength) > maxFilenameLength)
|
||||
|
|
@ -170,7 +170,7 @@ namespace LibationFileManager
|
|||
|
||||
var fullPath = Path.Combine(pathParts.Select(fileParts => string.Concat(fileParts)).Prepend(baseDir).ToArray());
|
||||
|
||||
return FileUtility.GetValidFilename(fullPath, replacements, fileExtension, returnFirstExisting);
|
||||
return FileUtility.GetValidFilename(fullPath, replacements, fileExtension, returnFirstExisting);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -186,7 +186,7 @@ namespace LibationFileManager
|
|||
foreach (var part in templateParts)
|
||||
{
|
||||
int slashIndex, lastIndex = 0;
|
||||
while((slashIndex = part.IndexOf(Path.DirectorySeparatorChar, lastIndex)) > -1)
|
||||
while ((slashIndex = part.IndexOf(Path.DirectorySeparatorChar, lastIndex)) > -1)
|
||||
{
|
||||
dir.Add(part[lastIndex..slashIndex]);
|
||||
RemoveSpaces(dir);
|
||||
|
|
@ -229,7 +229,7 @@ namespace LibationFileManager
|
|||
{
|
||||
original = parts[i];
|
||||
parts[i] = original.Replace(" ", " ");
|
||||
}while(original.Length != parts[i].Length);
|
||||
} while (original.Length != parts[i].Length);
|
||||
}
|
||||
|
||||
//Remove instances of double spaces at part boundaries
|
||||
|
|
@ -262,11 +262,12 @@ namespace LibationFileManager
|
|||
{ TemplateTags.AudibleTitle, lb => lb.Title },
|
||||
{ TemplateTags.AudibleSubtitle, lb => lb.Subtitle },
|
||||
{ TemplateTags.Author, lb => lb.Authors, NameListFormat.Formatter },
|
||||
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor },
|
||||
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor, FormattableFormatter },
|
||||
{ TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter },
|
||||
{ TemplateTags.FirstNarrator, lb => lb.FirstNarrator },
|
||||
{ TemplateTags.Series, lb => lb.SeriesName },
|
||||
{ TemplateTags.SeriesNumber, lb => lb.IsPodcastParent ? null : lb.SeriesNumber },
|
||||
{ TemplateTags.FirstNarrator, lb => lb.FirstNarrator, FormattableFormatter },
|
||||
{ TemplateTags.Series, lb => lb.Series, SeriesListFormat.Formatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, FormattableFormatter },
|
||||
{ TemplateTags.SeriesNumber, lb => lb.FirstSeries?.Number },
|
||||
{ TemplateTags.Language, lb => lb.Language },
|
||||
//Don't allow formatting of LanguageShort
|
||||
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
||||
|
|
@ -280,7 +281,7 @@ namespace LibationFileManager
|
|||
{ TemplateTags.DatePublished, lb => lb.DatePublished },
|
||||
{ TemplateTags.DateAdded, lb => lb.DateAdded },
|
||||
{ TemplateTags.FileDate, lb => lb.FileDate },
|
||||
};
|
||||
};
|
||||
|
||||
private static readonly List<TagCollection> chapterPropertyTags = new()
|
||||
{
|
||||
|
|
@ -290,7 +291,8 @@ namespace LibationFileManager
|
|||
{ TemplateTags.TitleShort, lb => getTitleShort(lb.Title) },
|
||||
{ TemplateTags.AudibleTitle, lb => lb.Title },
|
||||
{ TemplateTags.AudibleSubtitle, lb => lb.Subtitle },
|
||||
{ TemplateTags.Series, lb => lb.SeriesName },
|
||||
{ TemplateTags.Series, lb => lb.Series, SeriesListFormat.Formatter },
|
||||
{ TemplateTags.FirstSeries, lb => lb.FirstSeries, FormattableFormatter },
|
||||
},
|
||||
new PropertyTagCollection<MultiConvertFileProperties>(caseSensative: true, StringFormatter, IntegerFormatter, DateTimeFormatter)
|
||||
{
|
||||
|
|
@ -332,6 +334,9 @@ namespace LibationFileManager
|
|||
return language[..3].ToUpper();
|
||||
}
|
||||
|
||||
private static string FormattableFormatter(ITemplateTag templateTag, IFormattable? value, string formatString)
|
||||
=> value?.ToString(formatString, null) ?? "";
|
||||
|
||||
private static string StringFormatter(ITemplateTag templateTag, string value, string formatString)
|
||||
{
|
||||
if (value is null) return "";
|
||||
|
|
@ -368,7 +373,7 @@ namespace LibationFileManager
|
|||
|
||||
public class FolderTemplate : Templates, ITemplate
|
||||
{
|
||||
public static string Name { get; }= "Folder Template";
|
||||
public static string Name { get; } = "Folder Template";
|
||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)) ?? "";
|
||||
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||
public static IEnumerable<TagCollection> TagCollections
|
||||
Loading…
Add table
Add a link
Reference in a new issue