Add codec tag and use real bitrate/samplerate (#1227)
This commit is contained in:
parent
f4dafac28f
commit
3982edd0f1
@ -39,8 +39,8 @@ namespace FileLiberator
|
|||||||
/// Path: in progress directory.
|
/// Path: in progress directory.
|
||||||
/// File name: final file name.
|
/// File name: final file name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
public static string GetInProgressFilename(this AudioFileStorage _, LibraryBookDto libraryBook, string extension)
|
||||||
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
|
=> Templates.File.GetFilename(libraryBook, AudibleFileStorage.DecryptInProgressDirectory, extension, returnFirstExisting: true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file does not exist
|
/// PDF: audio file does not exist
|
||||||
@ -48,6 +48,12 @@ namespace FileLiberator
|
|||||||
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBook libraryBook, string extension)
|
||||||
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension);
|
=> Templates.File.GetFilename(libraryBook.ToDto(), AudibleFileStorage.BooksDirectory, extension);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PDF: audio file does not exist
|
||||||
|
/// </summary>
|
||||||
|
public static string GetBooksDirectoryFilename(this AudioFileStorage _, LibraryBookDto dto, string extension)
|
||||||
|
=> Templates.File.GetFilename(dto, AudibleFileStorage.BooksDirectory, extension);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PDF: audio file already exists
|
/// PDF: audio file already exists
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -47,13 +47,18 @@ namespace FileLiberator
|
|||||||
if (libraryBook.Book.Audio_Exists())
|
if (libraryBook.Book.Audio_Exists())
|
||||||
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
return new StatusHandler { "Cannot find decrypt. Final audio file already exists" };
|
||||||
|
|
||||||
|
downloadValidation(libraryBook);
|
||||||
|
var api = await libraryBook.GetApiAsync();
|
||||||
|
var config = Configuration.Instance;
|
||||||
|
using var downloadOptions = await DownloadOptions.InitiateDownloadAsync(api, config, libraryBook);
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
FilePathCache.Inserted += FilePathCache_Inserted;
|
FilePathCache.Inserted += FilePathCache_Inserted;
|
||||||
FilePathCache.Removed += FilePathCache_Removed;
|
FilePathCache.Removed += FilePathCache_Removed;
|
||||||
|
|
||||||
success = await downloadAudiobookAsync(libraryBook);
|
success = await downloadAudiobookAsync(api, config, downloadOptions);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -78,12 +83,12 @@ namespace FileLiberator
|
|||||||
var finalStorageDir = getDestinationDirectory(libraryBook);
|
var finalStorageDir = getDestinationDirectory(libraryBook);
|
||||||
|
|
||||||
var moveFilesTask = Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
var moveFilesTask = Task.Run(() => moveFilesToBooksDir(libraryBook, entries));
|
||||||
Task[] finalTasks = new[]
|
Task[] finalTasks =
|
||||||
{
|
[
|
||||||
Task.Run(() => downloadCoverArt(libraryBook)),
|
Task.Run(() => downloadCoverArt(downloadOptions)),
|
||||||
moveFilesTask,
|
moveFilesTask,
|
||||||
Task.Run(() => WindowsDirectory.SetCoverAsFolderIcon(libraryBook.Book.PictureId, finalStorageDir))
|
Task.Run(() => WindowsDirectory.SetCoverAsFolderIcon(libraryBook.Book.PictureId, finalStorageDir))
|
||||||
};
|
];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -116,16 +121,11 @@ namespace FileLiberator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> downloadAudiobookAsync(LibraryBook libraryBook)
|
|
||||||
|
|
||||||
|
private async Task<bool> downloadAudiobookAsync(AudibleApi.Api api, Configuration config, DownloadOptions dlOptions)
|
||||||
{
|
{
|
||||||
var config = Configuration.Instance;
|
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(dlOptions.LibraryBookDto, dlOptions.OutputFormat.ToString().ToLower());
|
||||||
|
|
||||||
downloadValidation(libraryBook);
|
|
||||||
|
|
||||||
var api = await libraryBook.GetApiAsync();
|
|
||||||
|
|
||||||
using var dlOptions = await DownloadOptions.InitiateDownloadAsync(api, libraryBook, config);
|
|
||||||
var outFileName = AudibleFileStorage.Audio.GetInProgressFilename(libraryBook, dlOptions.OutputFormat.ToString().ToLower());
|
|
||||||
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
var cacheDir = AudibleFileStorage.DownloadsInProgressDirectory;
|
||||||
|
|
||||||
if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine)
|
if (dlOptions.DrmType is not DrmType.Adrm and not DrmType.Widevine)
|
||||||
@ -149,7 +149,7 @@ namespace FileLiberator
|
|||||||
abDownloader.RetrievedAuthors += OnAuthorsDiscovered;
|
abDownloader.RetrievedAuthors += OnAuthorsDiscovered;
|
||||||
abDownloader.RetrievedNarrators += OnNarratorsDiscovered;
|
abDownloader.RetrievedNarrators += OnNarratorsDiscovered;
|
||||||
abDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
abDownloader.RetrievedCoverArt += AaxcDownloader_RetrievedCoverArt;
|
||||||
abDownloader.FileCreated += (_, path) => OnFileCreated(libraryBook, path);
|
abDownloader.FileCreated += (_, path) => OnFileCreated(dlOptions.LibraryBook, path);
|
||||||
|
|
||||||
// REAL WORK DONE HERE
|
// REAL WORK DONE HERE
|
||||||
var success = await abDownloader.RunAsync();
|
var success = await abDownloader.RunAsync();
|
||||||
@ -158,12 +158,12 @@ namespace FileLiberator
|
|||||||
{
|
{
|
||||||
var metadataFile = LibationFileManager.Templates.Templates.File.GetFilename(dlOptions.LibraryBookDto, Path.GetDirectoryName(outFileName), ".metadata.json");
|
var metadataFile = LibationFileManager.Templates.Templates.File.GetFilename(dlOptions.LibraryBookDto, Path.GetDirectoryName(outFileName), ".metadata.json");
|
||||||
|
|
||||||
var item = await api.GetCatalogProductAsync(libraryBook.Book.AudibleProductId, AudibleApi.CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
|
var item = await api.GetCatalogProductAsync(dlOptions.LibraryBook.Book.AudibleProductId, AudibleApi.CatalogOptions.ResponseGroupOptions.ALL_OPTIONS);
|
||||||
item.SourceJson.Add(nameof(ContentMetadata.ChapterInfo), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ChapterInfo));
|
item.SourceJson.Add(nameof(ContentMetadata.ChapterInfo), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ChapterInfo));
|
||||||
item.SourceJson.Add(nameof(ContentMetadata.ContentReference), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ContentReference));
|
item.SourceJson.Add(nameof(ContentMetadata.ContentReference), Newtonsoft.Json.Linq.JObject.FromObject(dlOptions.ContentMetadata.ContentReference));
|
||||||
|
|
||||||
File.WriteAllText(metadataFile, item.SourceJson.ToString());
|
File.WriteAllText(metadataFile, item.SourceJson.ToString());
|
||||||
OnFileCreated(libraryBook, metadataFile);
|
OnFileCreated(dlOptions.LibraryBook, metadataFile);
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -280,7 +280,7 @@ namespace FileLiberator
|
|||||||
private static FilePathCache.CacheEntry getFirstAudioFile(IEnumerable<FilePathCache.CacheEntry> entries)
|
private static FilePathCache.CacheEntry getFirstAudioFile(IEnumerable<FilePathCache.CacheEntry> entries)
|
||||||
=> entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
=> entries.FirstOrDefault(f => f.FileType == FileType.Audio);
|
||||||
|
|
||||||
private static void downloadCoverArt(LibraryBook libraryBook)
|
private static void downloadCoverArt(DownloadOptions options)
|
||||||
{
|
{
|
||||||
if (!Configuration.Instance.DownloadCoverArt) return;
|
if (!Configuration.Instance.DownloadCoverArt) return;
|
||||||
|
|
||||||
@ -288,24 +288,24 @@ namespace FileLiberator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var destinationDir = getDestinationDirectory(libraryBook);
|
var destinationDir = getDestinationDirectory(options.LibraryBook);
|
||||||
coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(libraryBook, ".jpg");
|
coverPath = AudibleFileStorage.Audio.GetBooksDirectoryFilename(options.LibraryBookDto, ".jpg");
|
||||||
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
coverPath = Path.Combine(destinationDir, Path.GetFileName(coverPath));
|
||||||
|
|
||||||
if (File.Exists(coverPath))
|
if (File.Exists(coverPath))
|
||||||
FileUtility.SaferDelete(coverPath);
|
FileUtility.SaferDelete(coverPath);
|
||||||
|
|
||||||
var picBytes = PictureStorage.GetPictureSynchronously(new(libraryBook.Book.PictureLarge ?? libraryBook.Book.PictureId, PictureSize.Native));
|
var picBytes = PictureStorage.GetPictureSynchronously(new(options.LibraryBook.Book.PictureLarge ?? options.LibraryBook.Book.PictureId, PictureSize.Native));
|
||||||
if (picBytes.Length > 0)
|
if (picBytes.Length > 0)
|
||||||
{
|
{
|
||||||
File.WriteAllBytes(coverPath, picBytes);
|
File.WriteAllBytes(coverPath, picBytes);
|
||||||
SetFileTime(libraryBook, coverPath);
|
SetFileTime(options.LibraryBook, coverPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
//Failure to download cover art should not be considered a failure to download the book
|
//Failure to download cover art should not be considered a failure to download the book
|
||||||
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {libraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
Serilog.Log.Logger.Error(ex, $"Error downloading cover art of {options.LibraryBook.Book.AudibleProductId} to {coverPath} catalog product.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
@ -23,7 +24,7 @@ public partial class DownloadOptions
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initiate an audiobook download from the audible api.
|
/// Initiate an audiobook download from the audible api.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<DownloadOptions> InitiateDownloadAsync(Api api, LibraryBook libraryBook, Configuration config)
|
public static async Task<DownloadOptions> InitiateDownloadAsync(Api api, Configuration config, LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
var license = await ChooseContent(api, libraryBook, config);
|
var license = await ChooseContent(api, libraryBook, config);
|
||||||
var options = BuildDownloadOptions(libraryBook, config, license);
|
var options = BuildDownloadOptions(libraryBook, config, license);
|
||||||
@ -172,6 +173,14 @@ public partial class DownloadOptions
|
|||||||
RuntimeLength = TimeSpan.FromMilliseconds(contentLic.ContentMetadata.ChapterInfo.RuntimeLengthMs),
|
RuntimeLength = TimeSpan.FromMilliseconds(contentLic.ContentMetadata.ChapterInfo.RuntimeLengthMs),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
dlOptions.LibraryBookDto.Codec = contentLic.ContentMetadata.ContentReference.Codec;
|
||||||
|
if (TryGetAudioInfo(contentLic.ContentMetadata.ContentUrl, out int? bitrate, out int? sampleRate, out int? channels))
|
||||||
|
{
|
||||||
|
dlOptions.LibraryBookDto.BitRate = bitrate;
|
||||||
|
dlOptions.LibraryBookDto.SampleRate = sampleRate;
|
||||||
|
dlOptions.LibraryBookDto.Channels = channels;
|
||||||
|
}
|
||||||
|
|
||||||
var titleConcat = config.CombineNestedChapterTitles ? ": " : null;
|
var titleConcat = config.CombineNestedChapterTitles ? ": " : null;
|
||||||
var chapters
|
var chapters
|
||||||
= flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters, titleConcat)
|
= flattenChapters(contentLic.ContentMetadata.ChapterInfo.Chapters, titleConcat)
|
||||||
@ -198,6 +207,43 @@ public partial class DownloadOptions
|
|||||||
return dlOptions;
|
return dlOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The most reliable way to get these audio file properties is from the filename itself.
|
||||||
|
/// Using AAXClean to read the metadata works well for everything except AC-4 bitrate.
|
||||||
|
/// </summary>
|
||||||
|
private static bool TryGetAudioInfo(ContentUrl? contentUrl, out int? bitrate, out int? sampleRate, out int? channels)
|
||||||
|
{
|
||||||
|
bitrate = sampleRate = channels = null;
|
||||||
|
|
||||||
|
if (contentUrl?.OfflineUrl is not string url || !Uri.TryCreate(url, default, out var uri))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var file = Path.GetFileName(uri.LocalPath);
|
||||||
|
|
||||||
|
var match = AdrmAudioProperties().Match(file);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
bitrate = int.Parse(match.Groups[1].Value);
|
||||||
|
sampleRate = int.Parse(match.Groups[2].Value);
|
||||||
|
channels = int.Parse(match.Groups[3].Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ((match = WidevineAudioProperties().Match(file)).Success)
|
||||||
|
{
|
||||||
|
bitrate = int.Parse(match.Groups[2].Value);
|
||||||
|
sampleRate = int.Parse(match.Groups[1].Value) * 1000;
|
||||||
|
channels = match.Groups[3].Value switch
|
||||||
|
{
|
||||||
|
"ec3" => 6,
|
||||||
|
"ac4" => 3,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static LameConfig GetLameOptions(Configuration config)
|
public static LameConfig GetLameOptions(Configuration config)
|
||||||
{
|
{
|
||||||
LameConfig lameConfig = new()
|
LameConfig lameConfig = new()
|
||||||
@ -355,4 +401,9 @@ public partial class DownloadOptions
|
|||||||
|
|
||||||
static double RelativePercentDifference(long num1, long num2)
|
static double RelativePercentDifference(long num1, long num2)
|
||||||
=> Math.Abs(num1 - num2) / (double)(num1 + num2);
|
=> Math.Abs(num1 - num2) / (double)(num1 + num2);
|
||||||
|
|
||||||
|
[GeneratedRegex(@".+_(\d+)_(\d+)-(\w+).mp4", RegexOptions.Singleline | RegexOptions.IgnoreCase)]
|
||||||
|
private static partial Regex WidevineAudioProperties();
|
||||||
|
[GeneratedRegex(@".+_lc_(\d+)_(\d+)_(\d+).aax", RegexOptions.Singleline | RegexOptions.IgnoreCase)]
|
||||||
|
private static partial Regex AdrmAudioProperties();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,9 +55,6 @@ namespace FileLiberator
|
|||||||
IsPodcastParent = libraryBook.Book.IsEpisodeParent(),
|
IsPodcastParent = libraryBook.Book.IsEpisodeParent(),
|
||||||
IsPodcast = libraryBook.Book.IsEpisodeChild() || libraryBook.Book.IsEpisodeParent(),
|
IsPodcast = libraryBook.Book.IsEpisodeChild() || libraryBook.Book.IsEpisodeParent(),
|
||||||
|
|
||||||
BitRate = libraryBook.Book.AudioFormat.Bitrate,
|
|
||||||
SampleRate = libraryBook.Book.AudioFormat.SampleRate,
|
|
||||||
Channels = libraryBook.Book.AudioFormat.Channels,
|
|
||||||
Language = libraryBook.Book.Language
|
Language = libraryBook.Book.Language
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,10 @@ public class BookDto
|
|||||||
public bool IsPodcastParent { get; set; }
|
public bool IsPodcastParent { get; set; }
|
||||||
public bool IsPodcast { get; set; }
|
public bool IsPodcast { get; set; }
|
||||||
|
|
||||||
public int BitRate { get; set; }
|
public int? BitRate { get; set; }
|
||||||
public int SampleRate { get; set; }
|
public int? SampleRate { get; set; }
|
||||||
public int Channels { get; set; }
|
public int? Channels { get; set; }
|
||||||
|
public string? Codec { get; set; }
|
||||||
public DateTime FileDate { get; set; } = DateTime.Now;
|
public DateTime FileDate { get; set; } = DateTime.Now;
|
||||||
public DateTime? DatePublished { get; set; }
|
public DateTime? DatePublished { get; set; }
|
||||||
public string? Language { get; set; }
|
public string? Language { get; set; }
|
||||||
|
|||||||
@ -36,9 +36,10 @@ namespace LibationFileManager.Templates
|
|||||||
public static TemplateTags Series { get; } = new TemplateTags("series", "All series to which the book belongs (if any)");
|
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 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 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 Bitrate { get; } = new TemplateTags("bitrate", "Audiobook's source bitrate");
|
||||||
public static TemplateTags SampleRate { get; } = new TemplateTags("samplerate", "File's orig. sample rate");
|
public static TemplateTags SampleRate { get; } = new TemplateTags("samplerate", "Audiobook's source sample rate");
|
||||||
public static TemplateTags Channels { get; } = new TemplateTags("channels", "Number of audio channels");
|
public static TemplateTags Channels { get; } = new TemplateTags("channels", "Audiobook's source audio channel count");
|
||||||
|
public static TemplateTags Codec { get; } = new TemplateTags("codec", "Audiobook's source codec");
|
||||||
public static TemplateTags Account { get; } = new TemplateTags("account", "Audible account of this book");
|
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 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");
|
||||||
|
|||||||
@ -271,9 +271,6 @@ namespace LibationFileManager.Templates
|
|||||||
{ TemplateTags.Language, lb => lb.Language },
|
{ TemplateTags.Language, lb => lb.Language },
|
||||||
//Don't allow formatting of LanguageShort
|
//Don't allow formatting of LanguageShort
|
||||||
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
||||||
{ TemplateTags.Bitrate, lb => (int?)(lb.IsPodcastParent ? null : lb.BitRate) },
|
|
||||||
{ TemplateTags.SampleRate, lb => (int?)(lb.IsPodcastParent ? null : lb.SampleRate) },
|
|
||||||
{ TemplateTags.Channels, lb => (int?)(lb.IsPodcastParent ? null : lb.Channels) },
|
|
||||||
{ TemplateTags.Account, lb => lb.Account },
|
{ TemplateTags.Account, lb => lb.Account },
|
||||||
{ TemplateTags.AccountNickname, lb => lb.AccountNickname },
|
{ TemplateTags.AccountNickname, lb => lb.AccountNickname },
|
||||||
{ TemplateTags.Locale, lb => lb.Locale },
|
{ TemplateTags.Locale, lb => lb.Locale },
|
||||||
@ -283,6 +280,15 @@ namespace LibationFileManager.Templates
|
|||||||
{ TemplateTags.FileDate, lb => lb.FileDate },
|
{ TemplateTags.FileDate, lb => lb.FileDate },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly PropertyTagCollection<LibraryBookDto> audioFilePropertyTags =
|
||||||
|
new(caseSensative: true, StringFormatter, IntegerFormatter)
|
||||||
|
{
|
||||||
|
{ TemplateTags.Bitrate, lb => lb.BitRate },
|
||||||
|
{ TemplateTags.SampleRate, lb => lb.SampleRate },
|
||||||
|
{ TemplateTags.Channels, lb => lb.Channels },
|
||||||
|
{ TemplateTags.Codec, lb => lb.Codec },
|
||||||
|
};
|
||||||
|
|
||||||
private static readonly List<TagCollection> chapterPropertyTags = new()
|
private static readonly List<TagCollection> chapterPropertyTags = new()
|
||||||
{
|
{
|
||||||
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
|
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
|
||||||
@ -376,8 +382,7 @@ namespace LibationFileManager.Templates
|
|||||||
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 Description { get; } = Configuration.GetDescription(nameof(Configuration.FolderTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||||
public static IEnumerable<TagCollection> TagCollections
|
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, conditionalTags, folderConditionalTags];
|
||||||
=> new TagCollection[] { filePropertyTags, conditionalTags, folderConditionalTags };
|
|
||||||
|
|
||||||
public override IEnumerable<string> Errors
|
public override IEnumerable<string> Errors
|
||||||
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;
|
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;
|
||||||
@ -396,7 +401,7 @@ namespace LibationFileManager.Templates
|
|||||||
public static string Name { get; } = "File Template";
|
public static string Name { get; } = "File Template";
|
||||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate)) ?? "";
|
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.FileTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
||||||
public static IEnumerable<TagCollection> TagCollections { get; } = new TagCollection[] { filePropertyTags, conditionalTags };
|
public static IEnumerable<TagCollection> TagCollections { get; } = [filePropertyTags, audioFilePropertyTags, conditionalTags];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ChapterFileTemplate : Templates, ITemplate
|
public class ChapterFileTemplate : Templates, ITemplate
|
||||||
@ -404,7 +409,8 @@ namespace LibationFileManager.Templates
|
|||||||
public static string Name { get; } = "Chapter File Template";
|
public static string Name { get; } = "Chapter File Template";
|
||||||
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate)) ?? "";
|
public static string Description { get; } = Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate)) ?? "";
|
||||||
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||||
public static IEnumerable<TagCollection> TagCollections { get; } = chapterPropertyTags.Append(filePropertyTags).Append(conditionalTags);
|
public static IEnumerable<TagCollection> TagCollections { get; }
|
||||||
|
= chapterPropertyTags.Append(filePropertyTags).Append(audioFilePropertyTags).Append(conditionalTags);
|
||||||
|
|
||||||
public override IEnumerable<string> Warnings
|
public override IEnumerable<string> Warnings
|
||||||
=> NamingTemplate.TagsInUse.Any(t => t.TagName.In(TemplateTags.ChNumber.TagName, TemplateTags.ChNumber0.TagName))
|
=> NamingTemplate.TagsInUse.Any(t => t.TagName.In(TemplateTags.ChNumber.TagName, TemplateTags.ChNumber0.TagName))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user