Enhancement: add if-series conditional logic to custom file naming. Issue #151
This commit is contained in:
parent
e67eac92fd
commit
2567ccb44c
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<Version>6.4.3.1</Version>
|
<Version>6.4.4.1</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -167,8 +167,8 @@ namespace ApplicationServices
|
|||||||
{
|
{
|
||||||
logTime("importIntoDbAsync -- pre db");
|
logTime("importIntoDbAsync -- pre db");
|
||||||
using var context = DbContexts.GetContext();
|
using var context = DbContexts.GetContext();
|
||||||
var libraryImporter = new LibraryBookImporter(context);
|
var libraryBookImporter = new LibraryBookImporter(context);
|
||||||
var newCount = await Task.Run(() => libraryImporter.Import(importItems));
|
var newCount = await Task.Run(() => libraryBookImporter.Import(importItems));
|
||||||
logTime("importIntoDbAsync -- post Import()");
|
logTime("importIntoDbAsync -- post Import()");
|
||||||
var qtyChanges = context.SaveChanges();
|
var qtyChanges = context.SaveChanges();
|
||||||
logTime("importIntoDbAsync -- post SaveChanges");
|
logTime("importIntoDbAsync -- post SaveChanges");
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AudibleApi" Version="2.3.4.1" />
|
<PackageReference Include="AudibleApi" Version="2.3.5.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dinah.Core" Version="3.0.1.1" />
|
<PackageReference Include="Dinah.Core" Version="3.0.2.1" />
|
||||||
<PackageReference Include="Polly" Version="7.2.2" />
|
<PackageReference Include="Polly" Version="7.2.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@ -21,8 +21,8 @@ namespace LibationFileManager
|
|||||||
// putting these first is the incredibly lazy way to make them show up first in the EditTemplateDialog
|
// putting these first is the incredibly lazy way to make them show up first in the EditTemplateDialog
|
||||||
public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters", true);
|
public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters", true);
|
||||||
public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title", true);
|
public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title", true);
|
||||||
public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter number", true);
|
public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter #", true);
|
||||||
public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter number with leading zeros", true);
|
public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter # with leading zeros", true);
|
||||||
|
|
||||||
public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID");
|
public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID");
|
||||||
public static TemplateTags Title { get; } = new TemplateTags("title", "Full title");
|
public static TemplateTags Title { get; } = new TemplateTags("title", "Full title");
|
||||||
@ -36,5 +36,9 @@ namespace LibationFileManager
|
|||||||
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series");
|
public static TemplateTags SeriesNumber { get; } = new TemplateTags("series#", "Number order in series");
|
||||||
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 Locale { get; } = new TemplateTags("locale", "Region/country");
|
public static TemplateTags Locale { get; } = new TemplateTags("locale", "Region/country");
|
||||||
|
|
||||||
|
// Special case. Isn't mapped to a replacement in Templates.cs
|
||||||
|
// Included here for display by EditTemplateDialog
|
||||||
|
public static TemplateTags IfSeries { get; } = new TemplateTags("if series->...<-if series", "Only include if part of a series");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
using Dinah.Core;
|
using System;
|
||||||
using FileManager;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Dinah.Core;
|
||||||
|
using FileManager;
|
||||||
|
|
||||||
namespace LibationFileManager
|
namespace LibationFileManager
|
||||||
{
|
{
|
||||||
@ -106,12 +107,21 @@ namespace LibationFileManager
|
|||||||
: getFileNamingTemplate(libraryBookDto, template, null, null)
|
: getFileNamingTemplate(libraryBookDto, template, null, null)
|
||||||
.GetFilePath();
|
.GetFilePath();
|
||||||
|
|
||||||
|
private static Regex ifSeriesRegex { get; } = new Regex("<if series->(.*?)<-if series>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
internal static FileNamingTemplate getFileNamingTemplate(LibraryBookDto libraryBookDto, string template, string dirFullPath, string extension)
|
internal static FileNamingTemplate getFileNamingTemplate(LibraryBookDto libraryBookDto, string template, string dirFullPath, string extension)
|
||||||
{
|
{
|
||||||
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
ArgumentValidator.EnsureNotNullOrWhiteSpace(template, nameof(template));
|
||||||
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
|
ArgumentValidator.EnsureNotNull(libraryBookDto, nameof(libraryBookDto));
|
||||||
|
|
||||||
dirFullPath = dirFullPath?.Trim() ?? "";
|
dirFullPath = dirFullPath?.Trim() ?? "";
|
||||||
|
|
||||||
|
// for non-series, remove <if series-> and <-if series> tags and everything in between
|
||||||
|
// for series, remove <if series-> and <-if series> tags, what's in between will remain
|
||||||
|
template = ifSeriesRegex.Replace(
|
||||||
|
template,
|
||||||
|
string.IsNullOrWhiteSpace(libraryBookDto.SeriesName) ? "" : "$1");
|
||||||
|
|
||||||
var t = template + FileUtility.GetStandardizedExtension(extension);
|
var t = template + FileUtility.GetStandardizedExtension(extension);
|
||||||
var fullfilename = dirFullPath == "" ? t : Path.Combine(dirFullPath, t);
|
var fullfilename = dirFullPath == "" ? t : Path.Combine(dirFullPath, t);
|
||||||
|
|
||||||
|
|||||||
@ -114,12 +114,12 @@
|
|||||||
// columnHeader1
|
// columnHeader1
|
||||||
//
|
//
|
||||||
this.columnHeader1.Text = "Tag";
|
this.columnHeader1.Text = "Tag";
|
||||||
this.columnHeader1.Width = 90;
|
this.columnHeader1.Width = 137;
|
||||||
//
|
//
|
||||||
// columnHeader2
|
// columnHeader2
|
||||||
//
|
//
|
||||||
this.columnHeader2.Text = "Description";
|
this.columnHeader2.Text = "Description";
|
||||||
this.columnHeader2.Width = 230;
|
this.columnHeader2.Width = 170;
|
||||||
//
|
//
|
||||||
// richTextBox1
|
// richTextBox1
|
||||||
//
|
//
|
||||||
@ -137,14 +137,13 @@
|
|||||||
// warningsLbl
|
// warningsLbl
|
||||||
//
|
//
|
||||||
this.warningsLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
this.warningsLbl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
this.warningsLbl.AutoSize = true;
|
|
||||||
this.warningsLbl.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
this.warningsLbl.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
|
||||||
this.warningsLbl.ForeColor = System.Drawing.Color.Firebrick;
|
this.warningsLbl.ForeColor = System.Drawing.Color.Firebrick;
|
||||||
this.warningsLbl.Location = new System.Drawing.Point(346, 262);
|
this.warningsLbl.Location = new System.Drawing.Point(346, 262);
|
||||||
this.warningsLbl.Name = "warningsLbl";
|
this.warningsLbl.Name = "warningsLbl";
|
||||||
this.warningsLbl.Size = new System.Drawing.Size(14, 15);
|
this.warningsLbl.Size = new System.Drawing.Size(574, 77);
|
||||||
this.warningsLbl.TabIndex = 100;
|
this.warningsLbl.TabIndex = 6;
|
||||||
this.warningsLbl.Text = "6";
|
this.warningsLbl.Text = "[warnings]";
|
||||||
//
|
//
|
||||||
// exampleLbl
|
// exampleLbl
|
||||||
//
|
//
|
||||||
|
|||||||
@ -18,7 +18,7 @@ namespace LibationWinForms
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable
|
internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable
|
||||||
{
|
{
|
||||||
#region implementation properties
|
#region implementation properties NOT exposed to the view
|
||||||
// hide from public fields from Data Source GUI with [Browsable(false)]
|
// hide from public fields from Data Source GUI with [Browsable(false)]
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
@ -28,10 +28,52 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Model properties exposed to the view
|
||||||
|
private Image _cover;
|
||||||
|
public Image Cover
|
||||||
|
{
|
||||||
|
get => _cover;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
_cover = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string ProductRating { get; }
|
||||||
|
public string PurchaseDate { get; }
|
||||||
|
public string MyRating { get; }
|
||||||
|
public string Series { get; }
|
||||||
|
public string Title { get; }
|
||||||
|
public string Length { get; }
|
||||||
|
public string Authors { get; }
|
||||||
|
public string Narrators { get; }
|
||||||
|
public string Category { get; }
|
||||||
|
public string Misc { get; }
|
||||||
|
public string Description { get; }
|
||||||
|
public string DisplayTags
|
||||||
|
{
|
||||||
|
get => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated);
|
||||||
|
set => Book.UserDefinedItem.Tags = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint
|
||||||
|
public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate
|
||||||
|
{
|
||||||
|
get => (LibraryCommands.Liberated_Status(LibraryBook.Book), LibraryCommands.Pdf_Status(LibraryBook.Book));
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
LibraryBook.Book.UserDefinedItem.BookStatus = value.BookStatus;
|
||||||
|
LibraryBook.Book.UserDefinedItem.PdfStatus = value.PdfStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
public event EventHandler Committed;
|
public event EventHandler Committed;
|
||||||
|
|
||||||
private Book Book => LibraryBook.Book;
|
private Book Book => LibraryBook.Book;
|
||||||
private Image _cover;
|
|
||||||
|
|
||||||
public GridEntry(LibraryBook libraryBook)
|
public GridEntry(LibraryBook libraryBook)
|
||||||
{
|
{
|
||||||
@ -147,51 +189,11 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Model properties exposed to the view
|
|
||||||
public Image Cover
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _cover;
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
_cover = value;
|
|
||||||
NotifyPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ProductRating { get; }
|
|
||||||
public string PurchaseDate { get; }
|
|
||||||
public string MyRating { get; }
|
|
||||||
public string Series { get; }
|
|
||||||
public string Title { get; }
|
|
||||||
public string Length { get; }
|
|
||||||
public string Authors { get; }
|
|
||||||
public string Narrators { get; }
|
|
||||||
public string Category { get; }
|
|
||||||
public string Misc { get; }
|
|
||||||
public string Description { get; }
|
|
||||||
public string DisplayTags
|
|
||||||
{
|
|
||||||
get => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated);
|
|
||||||
set => Book.UserDefinedItem.Tags = value;
|
|
||||||
}
|
|
||||||
// these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint
|
|
||||||
public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate
|
|
||||||
{
|
|
||||||
get => (LibraryCommands.Liberated_Status(LibraryBook.Book), LibraryCommands.Pdf_Status(LibraryBook.Book));
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
LibraryBook.Book.UserDefinedItem.BookStatus = value.BookStatus;
|
|
||||||
LibraryBook.Book.UserDefinedItem.PdfStatus = value.PdfStatus;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Data Sorting
|
#region Data Sorting
|
||||||
|
// These methods are implementation of Dinah.Core.DataBinding.IMemberComparable
|
||||||
|
// Used by Dinah.Core.DataBinding.SortableBindingList<T> for all sorting
|
||||||
|
public virtual object GetMemberValue(string memberName) => _memberValues[memberName]();
|
||||||
|
public virtual IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType];
|
||||||
|
|
||||||
private Dictionary<string, Func<object>> _memberValues { get; }
|
private Dictionary<string, Func<object>> _memberValues { get; }
|
||||||
|
|
||||||
@ -225,9 +227,6 @@ namespace LibationWinForms
|
|||||||
{ typeof(LiberatedStatus), new ObjectComparer<LiberatedStatus>() },
|
{ typeof(LiberatedStatus), new ObjectComparer<LiberatedStatus>() },
|
||||||
};
|
};
|
||||||
|
|
||||||
public virtual object GetMemberValue(string memberName) => _memberValues[memberName]();
|
|
||||||
public virtual IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType];
|
|
||||||
|
|
||||||
private static readonly string[] _sortPrefixIgnores = { "the", "a", "an" };
|
private static readonly string[] _sortPrefixIgnores = { "the", "a", "an" };
|
||||||
private static string GetSortName(string unformattedName)
|
private static string GetSortName(string unformattedName)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="2.1.1.1" />
|
<PackageReference Include="Dinah.Core.WindowsDesktop" Version="2.1.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -71,7 +71,7 @@ namespace LibationWinForms
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Liberate_Click(GridEntry liveGridEntry)
|
private static async Task Liberate_Click(GridEntry liveGridEntry)
|
||||||
{
|
{
|
||||||
var libraryBook = liveGridEntry.LibraryBook;
|
var libraryBook = liveGridEntry.LibraryBook;
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ namespace LibationWinForms
|
|||||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook);
|
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Details_Click(GridEntry liveGridEntry)
|
private static void Details_Click(GridEntry liveGridEntry)
|
||||||
{
|
{
|
||||||
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook);
|
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook);
|
||||||
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace TemplatesTests
|
|||||||
{
|
{
|
||||||
public static class Shared
|
public static class Shared
|
||||||
{
|
{
|
||||||
public static LibraryBookDto GetLibraryBook(string asin)
|
public static LibraryBookDto GetLibraryBook(string asin, string seriesName = "Sherlock Holmes")
|
||||||
=> new()
|
=> new()
|
||||||
{
|
{
|
||||||
Account = "my account",
|
Account = "my account",
|
||||||
@ -22,7 +22,7 @@ namespace TemplatesTests
|
|||||||
Locale = "us",
|
Locale = "us",
|
||||||
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },
|
||||||
Narrators = new List<string> { "Stephen Fry" },
|
Narrators = new List<string> { "Stephen Fry" },
|
||||||
SeriesName = "Sherlock Holmes",
|
SeriesName = seriesName ?? "",
|
||||||
SeriesNumber = "1"
|
SeriesNumber = "1"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -75,6 +75,24 @@ namespace TemplatesTests
|
|||||||
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension)
|
=> Templates.getFileNamingTemplate(GetLibraryBook(asin), template, dirFullPath, extension)
|
||||||
.GetFilePath()
|
.GetFilePath()
|
||||||
.Should().Be(expected);
|
.Should().Be(expected);
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void IfSeries_empty()
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", "Sherlock Holmes"), "foo<if series-><-if series>bar", @"C:\a\b", "ext")
|
||||||
|
.GetFilePath()
|
||||||
|
.Should().Be(@"C:\a\b\foobar.ext");
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void IfSeries_no_series()
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", ""), "foo<if series->-<series>-<id>-<-if series>bar", @"C:\a\b", "ext")
|
||||||
|
.GetFilePath()
|
||||||
|
.Should().Be(@"C:\a\b\foobar.ext");
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void IfSeries_with_series()
|
||||||
|
=> Templates.getFileNamingTemplate(GetLibraryBook("asin", "Sherlock Holmes"), "foo<if series->-<series>-<id>-<-if series>bar", @"C:\a\b", "ext")
|
||||||
|
.GetFilePath()
|
||||||
|
.Should().Be(@"C:\a\b\foo-Sherlock Holmes-asin-bar.ext");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user