diff --git a/Documentation/NamingTemplates.md b/Documentation/NamingTemplates.md index 0f26c62e..8d311613 100644 --- a/Documentation/NamingTemplates.md +++ b/Documentation/NamingTemplates.md @@ -25,8 +25,10 @@ These tags will be replaced in the template with the audiobook's values. |Tag|Description|Type| |-|-|-| |\ **†**|Audible book ID (ASIN)|Text| -|\|Full title|Text| +|\|Full title with subtitle|Text| |\|Title. Stop at first colon|Text| +|\<audible title\>|Audible's title (does not include subtitle)|Text| +|\<audible subtitle\>|Audible's subtitle|Text| |\<author\>|Author(s)|Name List| |\<first author\>|First author|Text| |\<narrator\>|Narrator(s)|Name List| diff --git a/Source/ApplicationServices/LibraryExporter.cs b/Source/ApplicationServices/LibraryExporter.cs index 713e2317..29a0d74f 100644 --- a/Source/ApplicationServices/LibraryExporter.cs +++ b/Source/ApplicationServices/LibraryExporter.cs @@ -35,6 +35,9 @@ namespace ApplicationServices [Name("Title")] public string Title { get; set; } + [Name("Subtitle")] + public string Subtitle { get; set; } + [Name("Authors")] public string AuthorNames { get; set; } @@ -123,6 +126,7 @@ namespace ApplicationServices AudibleProductId = a.Book.AudibleProductId, Locale = a.Book.Locale, Title = a.Book.Title, + Subtitle = a.Book.Subtitle, AuthorNames = a.Book.AuthorNames(), NarratorNames = a.Book.NarratorNames(), LengthInMinutes = a.Book.LengthInMinutes, @@ -198,6 +202,7 @@ namespace ApplicationServices nameof(ExportDto.AudibleProductId), nameof(ExportDto.Locale), nameof(ExportDto.Title), + nameof(ExportDto.Subtitle), nameof(ExportDto.AuthorNames), nameof(ExportDto.NarratorNames), nameof(ExportDto.LengthInMinutes), @@ -256,6 +261,7 @@ namespace ApplicationServices row.CreateCell(col++).SetCellValue(dto.AudibleProductId); row.CreateCell(col++).SetCellValue(dto.Locale); row.CreateCell(col++).SetCellValue(dto.Title); + row.CreateCell(col++).SetCellValue(dto.Subtitle); row.CreateCell(col++).SetCellValue(dto.AuthorNames); row.CreateCell(col++).SetCellValue(dto.NarratorNames); row.CreateCell(col++).SetCellValue(dto.LengthInMinutes); diff --git a/Source/ApplicationServices/RecordExporter.cs b/Source/ApplicationServices/RecordExporter.cs index 44101738..52506383 100644 --- a/Source/ApplicationServices/RecordExporter.cs +++ b/Source/ApplicationServices/RecordExporter.cs @@ -108,7 +108,7 @@ namespace ApplicationServices var recordsObj = new JObject { - { "title", libraryBook.Book.Title}, + { "title", libraryBook.Book.TitleWithSubtitle}, { "asin", libraryBook.Book.AudibleProductId}, { "exportTime", DateTime.Now}, { "records", JArray.FromObject(recordsEx) } diff --git a/Source/DataLayer/Configurations/BookConfig.cs b/Source/DataLayer/Configurations/BookConfig.cs index 55c13038..af5bad8a 100644 --- a/Source/DataLayer/Configurations/BookConfig.cs +++ b/Source/DataLayer/Configurations/BookConfig.cs @@ -20,6 +20,7 @@ namespace DataLayer.Configurations entity.Ignore(nameof(Book.Authors)); entity.Ignore(nameof(Book.Narrators)); entity.Ignore(nameof(Book.AudioFormat)); + entity.Ignore(nameof(Book.TitleWithSubtitle)); //// these don't seem to matter //entity.Ignore(nameof(Book.AuthorNames)); //entity.Ignore(nameof(Book.NarratorNames)); diff --git a/Source/DataLayer/EfClasses/Book.cs b/Source/DataLayer/EfClasses/Book.cs index 8b253411..2492a5dd 100644 --- a/Source/DataLayer/EfClasses/Book.cs +++ b/Source/DataLayer/EfClasses/Book.cs @@ -34,7 +34,10 @@ namespace DataLayer // immutable public string AudibleProductId { get; private set; } public string Title { get; private set; } - public string Description { get; private set; } + public string Subtitle { get; private set; } + private string _titleWithSubtitle; + public string TitleWithSubtitle => _titleWithSubtitle ??= string.IsNullOrEmpty(Subtitle) ? Title : $"{Title}: {Subtitle}"; + public string Description { get; private set; } public int LengthInMinutes { get; private set; } public ContentType ContentType { get; private set; } public string Locale { get; private set; } @@ -70,6 +73,7 @@ namespace DataLayer public Book( AudibleProductId audibleProductId, string title, + string subtitle, string description, int lengthInMinutes, ContentType contentType, @@ -98,8 +102,8 @@ namespace DataLayer Category = category; // simple assigns - Title = title.Trim() ?? ""; - Description = description?.Trim() ?? ""; + UpdateTitle(title, subtitle); + Description = description?.Trim() ?? ""; LengthInMinutes = lengthInMinutes; ContentType = contentType; @@ -107,10 +111,16 @@ namespace DataLayer ReplaceAuthors(authors); ReplaceNarrators(narrators); } + public void UpdateTitle(string title, string subtitle) + { + Title = title?.Trim() ?? ""; + Subtitle = subtitle?.Trim() ?? ""; + _titleWithSubtitle = null; + } - #region contributors, authors, narrators - // use uninitialised backing fields - this means we can detect if the collection was loaded - private HashSet<BookContributor> _contributorsLink; + #region contributors, authors, narrators + // use uninitialised backing fields - this means we can detect if the collection was loaded + private HashSet<BookContributor> _contributorsLink; // i'd like this to be internal but migration throws this exception when i try: // Value cannot be null. // Parameter name: property @@ -233,6 +243,6 @@ namespace DataLayer Category = category; } - public override string ToString() => $"[{AudibleProductId}] {Title}"; + public override string ToString() => $"[{AudibleProductId}] {TitleWithSubtitle}"; } } diff --git a/Source/DataLayer/EntityExtensions.cs b/Source/DataLayer/EntityExtensions.cs index d5b264cf..97b6ff8d 100644 --- a/Source/DataLayer/EntityExtensions.cs +++ b/Source/DataLayer/EntityExtensions.cs @@ -8,7 +8,7 @@ namespace DataLayer { public static class EntityExtensions { - public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title); + public static string TitleSortable(this Book book) => Formatters.GetSortName(book.Title + book.Subtitle); public static string AuthorNames(this Book book) => string.Join(", ", book.Authors.Select(a => a.Name)); public static string NarratorNames(this Book book) => string.Join(", ", book.Narrators.Select(n => n.Name)); @@ -62,7 +62,7 @@ namespace DataLayer max = Math.Max(max, 1); - var titles = libraryBooks.Select(lb => "- " + lb.Book.Title).ToList(); + var titles = libraryBooks.Select(lb => "- " + lb.Book.TitleWithSubtitle).ToList(); var titlesAgg = titles.Take(max).Aggregate((a, b) => $"{a}\r\n{b}"); if (titles.Count == max + 1) titlesAgg += $"\r\n\r\nand 1 other"; diff --git a/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.Designer.cs b/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.Designer.cs new file mode 100644 index 00000000..911c0b20 --- /dev/null +++ b/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.Designer.cs @@ -0,0 +1,416 @@ +// <auto-generated /> +using System; +using DataLayer; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DataLayer.Migrations +{ + [DbContext(typeof(LibationContext))] + [Migration("20230626171442_AddBookSubtitle")] + partial class AddBookSubtitle + { + /// <inheritdoc /> + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); + + modelBuilder.Entity("DataLayer.Book", b => + { + b.Property<int>("BookId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudibleProductId") + .HasColumnType("TEXT"); + + b.Property<int>("CategoryId") + .HasColumnType("INTEGER"); + + b.Property<int>("ContentType") + .HasColumnType("INTEGER"); + + b.Property<DateTime?>("DatePublished") + .HasColumnType("TEXT"); + + b.Property<string>("Description") + .HasColumnType("TEXT"); + + b.Property<bool>("IsAbridged") + .HasColumnType("INTEGER"); + + b.Property<string>("Language") + .HasColumnType("TEXT"); + + b.Property<int>("LengthInMinutes") + .HasColumnType("INTEGER"); + + b.Property<string>("Locale") + .HasColumnType("TEXT"); + + b.Property<string>("PictureId") + .HasColumnType("TEXT"); + + b.Property<string>("PictureLarge") + .HasColumnType("TEXT"); + + b.Property<string>("Subtitle") + .HasColumnType("TEXT"); + + b.Property<string>("Title") + .HasColumnType("TEXT"); + + b.Property<long>("_audioFormat") + .HasColumnType("INTEGER"); + + b.HasKey("BookId"); + + b.HasIndex("AudibleProductId"); + + b.HasIndex("CategoryId"); + + b.ToTable("Books"); + }); + + modelBuilder.Entity("DataLayer.BookContributor", b => + { + b.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b.Property<int>("ContributorId") + .HasColumnType("INTEGER"); + + b.Property<int>("Role") + .HasColumnType("INTEGER"); + + b.Property<byte>("Order") + .HasColumnType("INTEGER"); + + b.HasKey("BookId", "ContributorId", "Role"); + + b.HasIndex("BookId"); + + b.HasIndex("ContributorId"); + + b.ToTable("BookContributor"); + }); + + modelBuilder.Entity("DataLayer.Category", b => + { + b.Property<int>("CategoryId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudibleCategoryId") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .HasColumnType("TEXT"); + + b.Property<int?>("ParentCategoryCategoryId") + .HasColumnType("INTEGER"); + + b.HasKey("CategoryId"); + + b.HasIndex("AudibleCategoryId"); + + b.HasIndex("ParentCategoryCategoryId"); + + b.ToTable("Categories"); + + b.HasData( + new + { + CategoryId = -1, + AudibleCategoryId = "", + Name = "" + }); + }); + + modelBuilder.Entity("DataLayer.Contributor", b => + { + b.Property<int>("ContributorId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudibleContributorId") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .HasColumnType("TEXT"); + + b.HasKey("ContributorId"); + + b.HasIndex("Name"); + + b.ToTable("Contributors"); + + b.HasData( + new + { + ContributorId = -1, + Name = "" + }); + }); + + modelBuilder.Entity("DataLayer.LibraryBook", b => + { + b.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b.Property<bool>("AbsentFromLastScan") + .HasColumnType("INTEGER"); + + b.Property<string>("Account") + .HasColumnType("TEXT"); + + b.Property<DateTime>("DateAdded") + .HasColumnType("TEXT"); + + b.Property<bool>("IsDeleted") + .HasColumnType("INTEGER"); + + b.HasKey("BookId"); + + b.ToTable("LibraryBooks"); + }); + + modelBuilder.Entity("DataLayer.Series", b => + { + b.Property<int>("SeriesId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property<string>("AudibleSeriesId") + .HasColumnType("TEXT"); + + b.Property<string>("Name") + .HasColumnType("TEXT"); + + b.HasKey("SeriesId"); + + b.HasIndex("AudibleSeriesId"); + + b.ToTable("Series"); + }); + + modelBuilder.Entity("DataLayer.SeriesBook", b => + { + b.Property<int>("SeriesId") + .HasColumnType("INTEGER"); + + b.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b.Property<string>("Order") + .HasColumnType("TEXT"); + + b.HasKey("SeriesId", "BookId"); + + b.HasIndex("BookId"); + + b.HasIndex("SeriesId"); + + b.ToTable("SeriesBook"); + }); + + modelBuilder.Entity("DataLayer.Book", b => + { + b.HasOne("DataLayer.Category", "Category") + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.OwnsOne("DataLayer.Rating", "Rating", b1 => + { + b1.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b1.Property<float>("OverallRating") + .HasColumnType("REAL"); + + b1.Property<float>("PerformanceRating") + .HasColumnType("REAL"); + + b1.Property<float>("StoryRating") + .HasColumnType("REAL"); + + b1.HasKey("BookId"); + + b1.ToTable("Books"); + + b1.WithOwner() + .HasForeignKey("BookId"); + }); + + b.OwnsMany("DataLayer.Supplement", "Supplements", b1 => + { + b1.Property<int>("SupplementId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b1.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b1.Property<string>("Url") + .HasColumnType("TEXT"); + + b1.HasKey("SupplementId"); + + b1.HasIndex("BookId"); + + b1.ToTable("Supplement"); + + b1.WithOwner("Book") + .HasForeignKey("BookId"); + + b1.Navigation("Book"); + }); + + b.OwnsOne("DataLayer.UserDefinedItem", "UserDefinedItem", b1 => + { + b1.Property<int>("BookId") + .HasColumnType("INTEGER"); + + b1.Property<int>("BookStatus") + .HasColumnType("INTEGER"); + + b1.Property<DateTime?>("LastDownloaded") + .HasColumnType("TEXT"); + + b1.Property<string>("LastDownloadedVersion") + .HasColumnType("TEXT"); + + b1.Property<int?>("PdfStatus") + .HasColumnType("INTEGER"); + + b1.Property<string>("Tags") + .HasColumnType("TEXT"); + + b1.HasKey("BookId"); + + b1.ToTable("UserDefinedItem", (string)null); + + b1.WithOwner("Book") + .HasForeignKey("BookId"); + + b1.OwnsOne("DataLayer.Rating", "Rating", b2 => + { + b2.Property<int>("UserDefinedItemBookId") + .HasColumnType("INTEGER"); + + b2.Property<float>("OverallRating") + .HasColumnType("REAL"); + + b2.Property<float>("PerformanceRating") + .HasColumnType("REAL"); + + b2.Property<float>("StoryRating") + .HasColumnType("REAL"); + + b2.HasKey("UserDefinedItemBookId"); + + b2.ToTable("UserDefinedItem"); + + b2.WithOwner() + .HasForeignKey("UserDefinedItemBookId"); + }); + + b1.Navigation("Book"); + + b1.Navigation("Rating"); + }); + + b.Navigation("Category"); + + b.Navigation("Rating"); + + b.Navigation("Supplements"); + + b.Navigation("UserDefinedItem"); + }); + + modelBuilder.Entity("DataLayer.BookContributor", b => + { + b.HasOne("DataLayer.Book", "Book") + .WithMany("ContributorsLink") + .HasForeignKey("BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DataLayer.Contributor", "Contributor") + .WithMany("BooksLink") + .HasForeignKey("ContributorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + + b.Navigation("Contributor"); + }); + + modelBuilder.Entity("DataLayer.Category", b => + { + b.HasOne("DataLayer.Category", "ParentCategory") + .WithMany() + .HasForeignKey("ParentCategoryCategoryId"); + + b.Navigation("ParentCategory"); + }); + + modelBuilder.Entity("DataLayer.LibraryBook", b => + { + b.HasOne("DataLayer.Book", "Book") + .WithOne() + .HasForeignKey("DataLayer.LibraryBook", "BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("DataLayer.SeriesBook", b => + { + b.HasOne("DataLayer.Book", "Book") + .WithMany("SeriesLink") + .HasForeignKey("BookId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("DataLayer.Series", "Series") + .WithMany("BooksLink") + .HasForeignKey("SeriesId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("DataLayer.Book", b => + { + b.Navigation("ContributorsLink"); + + b.Navigation("SeriesLink"); + }); + + modelBuilder.Entity("DataLayer.Contributor", b => + { + b.Navigation("BooksLink"); + }); + + modelBuilder.Entity("DataLayer.Series", b => + { + b.Navigation("BooksLink"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.cs b/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.cs new file mode 100644 index 00000000..bd815674 --- /dev/null +++ b/Source/DataLayer/Migrations/20230626171442_AddBookSubtitle.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DataLayer.Migrations +{ + /// <inheritdoc /> + public partial class AddBookSubtitle : Migration + { + /// <inheritdoc /> + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn<string>( + name: "Subtitle", + table: "Books", + type: "TEXT", + nullable: true); + } + + /// <inheritdoc /> + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Subtitle", + table: "Books"); + } + } +} diff --git a/Source/DataLayer/Migrations/LibationContextModelSnapshot.cs b/Source/DataLayer/Migrations/LibationContextModelSnapshot.cs index 2a0a1869..65d792b1 100644 --- a/Source/DataLayer/Migrations/LibationContextModelSnapshot.cs +++ b/Source/DataLayer/Migrations/LibationContextModelSnapshot.cs @@ -15,7 +15,7 @@ namespace DataLayer.Migrations protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "7.0.3"); + modelBuilder.HasAnnotation("ProductVersion", "7.0.5"); modelBuilder.Entity("DataLayer.Book", b => { @@ -56,6 +56,9 @@ namespace DataLayer.Migrations b.Property<string>("PictureLarge") .HasColumnType("TEXT"); + b.Property<string>("Subtitle") + .HasColumnType("TEXT"); + b.Property<string>("Title") .HasColumnType("TEXT"); diff --git a/Source/DtoImporterService/BookImporter.cs b/Source/DtoImporterService/BookImporter.cs index 700ad7d4..14d5d196 100644 --- a/Source/DtoImporterService/BookImporter.cs +++ b/Source/DtoImporterService/BookImporter.cs @@ -118,7 +118,8 @@ namespace DtoImporterService { book = DbContext.Books.Add(new Book( new AudibleProductId(item.ProductId), - item.TitleWithSubtitle, + item.Title, + item.Subtitle, item.Description, item.LengthInMinutes, contentType, @@ -164,6 +165,9 @@ namespace DtoImporterService { var item = importItem.DtoItem; + // Update the book titles, since formatting can change + book.UpdateTitle(item.Title, item.Subtitle); + var codec = item.AvailableCodecs?.Max(f => AudioFormat.FromString(f.EnhancedCodec)) ?? new AudioFormat(); book.AudioFormat = codec; diff --git a/Source/FileLiberator/DownloadDecryptBook.cs b/Source/FileLiberator/DownloadDecryptBook.cs index 5f0902aa..6ffc7f9b 100644 --- a/Source/FileLiberator/DownloadDecryptBook.cs +++ b/Source/FileLiberator/DownloadDecryptBook.cs @@ -331,9 +331,9 @@ namespace FileLiberator string errorTitle() { var title - = (libraryBook.Book.Title.Length > 53) - ? $"{libraryBook.Book.Title.Truncate(50)}..." - : libraryBook.Book.Title; + = (libraryBook.Book.TitleWithSubtitle.Length > 53) + ? $"{libraryBook.Book.TitleWithSubtitle.Truncate(50)}..." + : libraryBook.Book.TitleWithSubtitle; var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]"; return errorBookTitle; }; diff --git a/Source/FileLiberator/Processable.cs b/Source/FileLiberator/Processable.cs index 724c0cdc..54d360bf 100644 --- a/Source/FileLiberator/Processable.cs +++ b/Source/FileLiberator/Processable.cs @@ -45,7 +45,7 @@ namespace FileLiberator Serilog.Log.Logger.Information("Begin " + nameof(ProcessSingleAsync) + " {@DebugInfo}", new { - libraryBook.Book.Title, + libraryBook.Book.TitleWithSubtitle, libraryBook.Book.AudibleProductId, libraryBook.Book.Locale, Account = libraryBook.Account?.ToMask() ?? "[empty]" diff --git a/Source/FileLiberator/UtilityExtensions.cs b/Source/FileLiberator/UtilityExtensions.cs index 358cdd60..3cef22ca 100644 --- a/Source/FileLiberator/UtilityExtensions.cs +++ b/Source/FileLiberator/UtilityExtensions.cs @@ -14,7 +14,7 @@ namespace FileLiberator public static (string id, string title, string locale, string account) LogFriendly(this LibraryBook libraryBook) => ( id: libraryBook.Book.AudibleProductId, - title: libraryBook.Book.Title, + title: libraryBook.Book.TitleWithSubtitle, locale: libraryBook.Book.Locale, account: libraryBook.Account.ToMask() ); @@ -40,7 +40,9 @@ namespace FileLiberator DateAdded = libraryBook.DateAdded, AudibleProductId = libraryBook.Book.AudibleProductId, - Title = libraryBook.Book.Title ?? "", + Title = libraryBook.Book.Title, + Subtitle = libraryBook.Book.Subtitle, + TitleWithSubtitle = libraryBook.Book.TitleWithSubtitle, Locale = libraryBook.Book.Locale, YearPublished = libraryBook.Book.DatePublished?.Year, DatePublished = libraryBook.Book.DatePublished, diff --git a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs index 0e26b28f..49833015 100644 --- a/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookDetailsDialog.axaml.cs @@ -21,7 +21,7 @@ namespace LibationAvalonia.Dialogs set { _libraryBook = value; - Title = _libraryBook.Book.Title; + Title = _libraryBook.Book.TitleWithSubtitle; DataContext = _viewModel = new BookDetailsDialogViewModel(_libraryBook); } } @@ -106,9 +106,11 @@ namespace LibationAvalonia.Dialogs var picture = PictureStorage.GetPictureSynchronously(new PictureDefinition(libraryBook.Book.PictureId, PictureSize._80x80)); Cover = AvaloniaUtils.TryLoadImageOrDefault(picture, PictureSize._80x80); + var title = string.IsNullOrEmpty(Book.Subtitle) ? Book.Title : $"{Book.Title}\r\n {Book.Subtitle}"; + //init book details DetailsText = @$" -Title: {Book.Title} +Title: {title} Author(s): {Book.AuthorNames()} Narrator(s): {Book.NarratorNames()} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} diff --git a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs index 0dc1e65f..bb04161e 100644 --- a/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs +++ b/Source/LibationAvalonia/Dialogs/BookRecordsDialog.axaml.cs @@ -37,7 +37,7 @@ namespace LibationAvalonia.Dialogs public BookRecordsDialog(LibraryBook libraryBook) : this() { this.libraryBook = libraryBook; - Title = $"{libraryBook.Book.Title} - Clips and Bookmarks"; + Title = $"{libraryBook.Book.TitleWithSubtitle} - Clips and Bookmarks"; Loaded += BookRecordsDialog_Loaded; } @@ -148,7 +148,7 @@ namespace LibationAvalonia.Dialogs await Dispatcher.UIThread.InvokeAsync(() => new FilePickerSaveOptions { Title = "Where to export book records", - SuggestedFileName = $"{libraryBook.Book.Title} - Records", + SuggestedFileName = $"{libraryBook.Book.TitleWithSubtitle} - Records", DefaultExtension = "xlsx", ShowOverwritePrompt = true, FileTypeChoices = new FilePickerFileType[] diff --git a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs index f8fc4159..80d16cf8 100644 --- a/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProcessBookViewModel.cs @@ -105,7 +105,7 @@ namespace LibationAvalonia.ViewModels LibraryBook = libraryBook; Logger = logme; - _title = LibraryBook.Book.Title; + _title = LibraryBook.Book.TitleWithSubtitle; _author = LibraryBook.Book.AuthorNames(); _narrator = LibraryBook.Book.NarratorNames(); @@ -305,7 +305,7 @@ namespace LibationAvalonia.ViewModels Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}"); - Title = libraryBook.Book.Title; + Title = libraryBook.Book.TitleWithSubtitle; Author = libraryBook.Book.AuthorNames(); Narrator = libraryBook.Book.NarratorNames(); } @@ -372,7 +372,7 @@ namespace LibationAvalonia.ViewModels : str; details = -$@" Title: {libraryBook.Book.Title} +$@" Title: {libraryBook.Book.TitleWithSubtitle} ID: {libraryBook.Book.AudibleProductId} Author: {trunc(libraryBook.Book.AuthorNames())} Narr: {trunc(libraryBook.Book.NarratorNames())}"; @@ -392,7 +392,7 @@ $@" Title: {libraryBook.Book.Title} { libraryBook.UpdateBookStatus(LiberatedStatus.Error); - Logger.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.Title}"); + Logger.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.TitleWithSubtitle}"); return ProcessBookResult.FailedSkip; } diff --git a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs index 0f86d4b5..d1005185 100644 --- a/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs +++ b/Source/LibationAvalonia/ViewModels/ProcessQueueViewModel.cs @@ -227,7 +227,7 @@ namespace LibationAvalonia.ViewModels else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage) { await MessageBox.Show(@$" -You were denied a content license for {nextBook.LibraryBook.Book.Title} +You were denied a content license for {nextBook.LibraryBook.Book.TitleWithSubtitle} This error appears to be caused by a temporary interruption of service that sometimes affects Libation's users. This type of error usually resolves itself in 1 to 2 days, and in the meantime you should still be able to access your books through Audible's website or app. ", diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 2e421f09..c5313917 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -159,7 +159,7 @@ namespace LibationAvalonia.Views var openFileDialogOptions = new FilePickerOpenOptions { - Title = $"Locate the audio file for '{entry.Book.Title}'", + Title = $"Locate the audio file for '{entry.Book.TitleWithSubtitle}'", AllowMultiple = false, SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix), FileTypeFilter = new FilePickerFileType[] diff --git a/Source/LibationFileManager/LibraryBookDto.cs b/Source/LibationFileManager/LibraryBookDto.cs index a4e7456e..0703873c 100644 --- a/Source/LibationFileManager/LibraryBookDto.cs +++ b/Source/LibationFileManager/LibraryBookDto.cs @@ -8,7 +8,9 @@ namespace LibationFileManager { public string AudibleProductId { get; set; } public string Title { get; set; } - public string Locale { 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; } diff --git a/Source/LibationFileManager/TemplateEditor[T].cs b/Source/LibationFileManager/TemplateEditor[T].cs index 5ee16095..e3d68dc1 100644 --- a/Source/LibationFileManager/TemplateEditor[T].cs +++ b/Source/LibationFileManager/TemplateEditor[T].cs @@ -57,7 +57,8 @@ namespace LibationFileManager DateAdded = new DateTime(2022, 6, 9, 0, 0, 0), DatePublished = new DateTime(2017, 2, 27, 0, 0, 0), AudibleProductId = "123456789", - Title = "A Study in Scarlet: A Sherlock Holmes Novel", + Title = "A Study in Scarlet", + Subtitle = "A Sherlock Holmes Novel", Locale = "us", YearPublished = 2017, Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" }, diff --git a/Source/LibationFileManager/TemplateTags.cs b/Source/LibationFileManager/TemplateTags.cs index f9320c56..725eb98d 100644 --- a/Source/LibationFileManager/TemplateTags.cs +++ b/Source/LibationFileManager/TemplateTags.cs @@ -2,13 +2,13 @@ namespace LibationFileManager { - public sealed class TemplateTags : ITemplateTag + public sealed class TemplateTags : ITemplateTag { public const string DEFAULT_DATE_FORMAT = "yyyy-MM-dd"; public string TagName { get; } public string DefaultValue { get; } - public string Description { get; } - public string Display { get; } + public string Description { get; } + public string Display { get; } private TemplateTags(string tagName, string description, string defaultValue = null, string display = null) { @@ -19,36 +19,38 @@ namespace LibationFileManager } public static TemplateTags ChCount { get; } = new TemplateTags("ch count", "Number of chapters"); - public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title"); - public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter #"); - public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter # with leading zeros"); + public static TemplateTags ChTitle { get; } = new TemplateTags("ch title", "Chapter title"); + public static TemplateTags ChNumber { get; } = new TemplateTags("ch#", "Chapter #"); + public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter # with leading zeros"); - public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID"); - public static TemplateTags Title { get; } = new TemplateTags("title", "Full title"); - public static TemplateTags TitleShort { get; } = new TemplateTags("title short", "Title. Stop at first colon"); - public static TemplateTags Author { get; } = new TemplateTags("author", "Author(s)"); - 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 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 YearPublished { get; } = new("year", "Year published"); + public static TemplateTags Id { get; } = new TemplateTags("id", "Audible ID"); + public static TemplateTags Title { get; } = new TemplateTags("title", "Full title with subtitle"); + public static TemplateTags TitleShort { get; } = new TemplateTags("title short", "Title. Stop at first colon"); + public static TemplateTags AudibleTitle { get; } = new TemplateTags("audible title", "Audible's title (does not include subtitle)"); + public static TemplateTags AudibleSubtitle { get; } = new TemplateTags("audible subtitle", "Audible's subtitle"); + public static TemplateTags Author { get; } = new TemplateTags("author", "Author(s)"); + 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 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 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"); public static TemplateTags FileDate { get; } = new TemplateTags("file date", "File date/time. e.g. yyyy-MM-dd HH-mm", $"<file date [{DEFAULT_DATE_FORMAT}]>", "<file date [...]>"); - public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{DEFAULT_DATE_FORMAT}]>", "<pub date [...]>"); - public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{DEFAULT_DATE_FORMAT}]>", "<date added [...]>"); - public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a book series or podcast", "<if series-><-if series>", "<if series->...<-if series>"); - public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<if podcast-><-if podcast>", "<if podcast->...<-if podcast>"); - public static TemplateTags IfPodcastParent { get; } = new TemplateTags("if podcastparent", "Only include if item is a podcast series parent", "<if podcastparent-><-if podcastparent>", "<if podcastparent->...<-if podcastparent>"); - public static TemplateTags IfBookseries { get; } = new TemplateTags("if bookseries", "Only include if part of a book series", "<if bookseries-><-if bookseries>", "<if bookseries->...<-if bookseries>"); - } + public static TemplateTags DatePublished { get; } = new TemplateTags("pub date", "Publication date. e.g. yyyy-MM-dd", $"<pub date [{DEFAULT_DATE_FORMAT}]>", "<pub date [...]>"); + public static TemplateTags DateAdded { get; } = new TemplateTags("date added", "Date added to your Audible account. e.g. yyyy-MM-dd", $"<date added [{DEFAULT_DATE_FORMAT}]>", "<date added [...]>"); + public static TemplateTags IfSeries { get; } = new TemplateTags("if series", "Only include if part of a book series or podcast", "<if series-><-if series>", "<if series->...<-if series>"); + public static TemplateTags IfPodcast { get; } = new TemplateTags("if podcast", "Only include if part of a podcast", "<if podcast-><-if podcast>", "<if podcast->...<-if podcast>"); + public static TemplateTags IfPodcastParent { get; } = new TemplateTags("if podcastparent", "Only include if item is a podcast series parent", "<if podcastparent-><-if podcastparent>", "<if podcastparent->...<-if podcastparent>"); + public static TemplateTags IfBookseries { get; } = new TemplateTags("if bookseries", "Only include if part of a book series", "<if bookseries-><-if bookseries>", "<if bookseries->...<-if bookseries>"); + } } diff --git a/Source/LibationFileManager/Templates.cs b/Source/LibationFileManager/Templates.cs index 97146032..2a24cf22 100644 --- a/Source/LibationFileManager/Templates.cs +++ b/Source/LibationFileManager/Templates.cs @@ -247,8 +247,10 @@ namespace LibationFileManager { //Don't allow formatting of Id { TemplateTags.Id, lb => lb.AudibleProductId, v => v }, - { TemplateTags.Title, lb => lb.Title }, + { TemplateTags.Title, lb => lb.TitleWithSubtitle }, { TemplateTags.TitleShort, lb => getTitleShort(lb.Title) }, + { TemplateTags.AudibleTitle, lb => lb.Title }, + { TemplateTags.AudibleSubtitle, lb => lb.Subtitle }, { TemplateTags.Author, lb => lb.Authors, NameListFormat.Formatter }, { TemplateTags.FirstAuthor, lb => lb.FirstAuthor }, { TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter }, @@ -274,8 +276,10 @@ namespace LibationFileManager { new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter) { - { TemplateTags.Title, lb => lb.Title }, + { TemplateTags.Title, lb => lb.TitleWithSubtitle }, { TemplateTags.TitleShort, lb => getTitleShort(lb.Title) }, + { TemplateTags.AudibleTitle, lb => lb.Title }, + { TemplateTags.AudibleSubtitle, lb => lb.Subtitle }, { TemplateTags.Series, lb => lb.SeriesName }, }, new PropertyTagCollection<MultiConvertFileProperties>(caseSensative: true, StringFormatter, IntegerFormatter, DateTimeFormatter) diff --git a/Source/LibationSearchEngine/SearchEngine.cs b/Source/LibationSearchEngine/SearchEngine.cs index 00b80db7..061957f2 100644 --- a/Source/LibationSearchEngine/SearchEngine.cs +++ b/Source/LibationSearchEngine/SearchEngine.cs @@ -35,7 +35,7 @@ namespace LibationSearchEngine { { FieldType.ID, lb => lb.Book.AudibleProductId.ToLowerInvariant(), nameof(Book.AudibleProductId), "ProductId", "Id", "ASIN" }, { FieldType.Raw, lb => lb.Book.AudibleProductId, _ID_ }, - { FieldType.String, lb => lb.Book.Title, nameof(Book.Title), "ProductId", "Id", "ASIN" }, + { FieldType.String, lb => lb.Book.TitleWithSubtitle, "Title", "ProductId", "Id", "ASIN" }, { FieldType.String, lb => lb.Book.AuthorNames(), "AuthorNames", "Author", "Authors" }, { FieldType.String, lb => lb.Book.NarratorNames(), "NarratorNames", "Narrator", "Narrators" }, { FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) }, diff --git a/Source/LibationUiBase/GridView/GridEntry[TStatus].cs b/Source/LibationUiBase/GridView/GridEntry[TStatus].cs index 01d89f01..7517dd25 100644 --- a/Source/LibationUiBase/GridView/GridEntry[TStatus].cs +++ b/Source/LibationUiBase/GridView/GridEntry[TStatus].cs @@ -105,7 +105,7 @@ namespace LibationUiBase.GridView Liberate = TStatus.Create(libraryBook); Liberate.Expanded = expanded; - Title = Book.Title; + Title = Book.TitleWithSubtitle; Series = Book.SeriesNames(includeIndex: true); SeriesOrder = new SeriesOrder(Book.SeriesLink); Length = GetBookLengthString(); diff --git a/Source/LibationUiBase/SeriesView/AyceButton.cs b/Source/LibationUiBase/SeriesView/AyceButton.cs index 07d4318c..263e6773 100644 --- a/Source/LibationUiBase/SeriesView/AyceButton.cs +++ b/Source/LibationUiBase/SeriesView/AyceButton.cs @@ -108,7 +108,7 @@ namespace LibationUiBase.SeriesView { Asin = seriesParent.AudibleProductId, Sequence = item.Relationships.FirstOrDefault(r => r.Asin == seriesParent.AudibleProductId)?.Sort?.ToString() ?? "0", - Title = seriesParent.Title + Title = seriesParent.TitleWithSubtitle } }; } diff --git a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs index be111a04..bad7ee86 100644 --- a/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs +++ b/Source/LibationWinForms/Dialogs/BookDetailsDialog.cs @@ -38,13 +38,14 @@ namespace LibationWinForms.Dialogs // 1st draft: lazily cribbed from GridEntry.ctor() private void initDetails() { - this.Text = Book.Title; + this.Text = Book.TitleWithSubtitle; (_, var picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80)); this.coverPb.Image = WinFormsUtil.TryLoadImageOrDefault(picture, PictureSize._80x80); + var title = string.IsNullOrEmpty(Book.Subtitle) ? Book.Title : $"{Book.Title}\r\n {Book.Subtitle}"; var t = @$" -Title: {Book.Title} +Title: {title} Author(s): {Book.AuthorNames()} Narrator(s): {Book.NarratorNames()} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} diff --git a/Source/LibationWinForms/Dialogs/BookRecordsDialog.cs b/Source/LibationWinForms/Dialogs/BookRecordsDialog.cs index bc54876e..a93e652e 100644 --- a/Source/LibationWinForms/Dialogs/BookRecordsDialog.cs +++ b/Source/LibationWinForms/Dialogs/BookRecordsDialog.cs @@ -45,7 +45,7 @@ namespace LibationWinForms.Dialogs { this.libraryBook = libraryBook; - Text = $"{libraryBook.Book.Title} - Clips and Bookmarks"; + Text = $"{libraryBook.Book.TitleWithSubtitle} - Clips and Bookmarks"; } private async void BookRecordsDialog_Shown(object sender, EventArgs e) @@ -182,7 +182,7 @@ namespace LibationWinForms.Dialogs { Title = "Where to export records", AddExtension = true, - FileName = $"{libraryBook.Book.Title} - Records", + FileName = $"{libraryBook.Book.TitleWithSubtitle} - Records", DefaultExt = "xlsx", Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*" }); diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 342c5fcf..75996447 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -176,7 +176,7 @@ namespace LibationWinForms.GridView { var openFileDialog = new OpenFileDialog { - Title = $"Locate the audio file for '{entry.Book.Title}'", + Title = $"Locate the audio file for '{entry.Book.TitleWithSubtitle}'", Filter = "All files (*.*)|*.*", FilterIndex = 1 }; diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs index 50b3e1b5..ecb372f5 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs @@ -77,7 +77,7 @@ namespace LibationWinForms.ProcessQueue LibraryBook = libraryBook; Logger = logme; - title = LibraryBook.Book.Title; + title = LibraryBook.Book.TitleWithSubtitle; authorNames = LibraryBook.Book.AuthorNames(); narratorNames = LibraryBook.Book.NarratorNames(); _bookText = $"{title}\r\nBy {authorNames}\r\nNarrated by {narratorNames}"; @@ -291,7 +291,7 @@ namespace LibationWinForms.ProcessQueue Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}"); - title = libraryBook.Book.Title; + title = libraryBook.Book.TitleWithSubtitle; authorNames = libraryBook.Book.AuthorNames(); narratorNames = libraryBook.Book.NarratorNames(); updateBookInfo(); @@ -359,7 +359,7 @@ namespace LibationWinForms.ProcessQueue : str; details = -$@" Title: {libraryBook.Book.Title} +$@" Title: {libraryBook.Book.TitleWithSubtitle} ID: {libraryBook.Book.AudibleProductId} Author: {trunc(libraryBook.Book.AuthorNames())} Narr: {trunc(libraryBook.Book.NarratorNames())}"; @@ -379,7 +379,7 @@ $@" Title: {libraryBook.Book.Title} { libraryBook.UpdateBookStatus(LiberatedStatus.Error); - Logger.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.Title}"); + Logger.Info($"Error. Skip: [{libraryBook.Book.AudibleProductId}] {libraryBook.Book.TitleWithSubtitle}"); return ProcessBookResult.FailedSkip; } diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index e05b23b7..da7bebbc 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -188,7 +188,7 @@ namespace LibationWinForms.ProcessQueue else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage) { MessageBox.Show(@$" -You were denied a content license for {nextBook.LibraryBook.Book.Title} +You were denied a content license for {nextBook.LibraryBook.Book.TitleWithSubtitle} This error appears to be caused by a temporary interruption of service that sometimes affects Libation's users. This type of error usually resolves itself in 1 to 2 days, and in the meantime you should still be able to access your books through Audible's website or app. ", diff --git a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs index aeb66acb..0864d632 100644 --- a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs +++ b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs @@ -463,7 +463,7 @@ namespace Templates_Other extension = FileUtility.GetStandardizedExtension(extension); var lbDto = GetLibraryBook(); - lbDto.Title = title; + lbDto.TitleWithSubtitle = title; lbDto.AudibleProductId = "ID123456"; Templates.TryGetTemplate<Templates.FolderTemplate>(template, out var fileNamingTemplate).Should().BeTrue(); @@ -491,7 +491,7 @@ namespace Templates_Other var template = Path.GetFileNameWithoutExtension(originalPath) + " - <ch# 0> - <title>" + estension; var lbDto = GetLibraryBook(); - lbDto.Title = suffix; + lbDto.TitleWithSubtitle = suffix; Templates.TryGetTemplate<Templates.ChapterFileTemplate>(template, out var chapterFileTemplate).Should().BeTrue(); @@ -508,7 +508,7 @@ namespace Templates_Other if (Environment.OSVersion.Platform == platformID) { var lbDto = GetLibraryBook(); - lbDto.Title = @"s\l/a\s/h\e/s"; + lbDto.TitleWithSubtitle = @"s\l/a\s/h\e/s"; var directory = Path.GetDirectoryName(inStr); var fileName = Path.GetFileName(inStr);