Integrate new Title and Subtitle properties into Libation

This commit is contained in:
Mbucari 2023-06-29 19:55:16 -06:00
parent 2195574422
commit 83fa73cef5
27 changed files with 109 additions and 98 deletions

View File

@ -25,8 +25,10 @@ These tags will be replaced in the template with the audiobook's values.
|Tag|Description|Type| |Tag|Description|Type|
|-|-|-| |-|-|-|
|\<id\> **†**|Audible book ID (ASIN)|Text| |\<id\> **†**|Audible book ID (ASIN)|Text|
|\<title\>|Full title|Text| |\<title\>|Full title with subtitle|Text|
|\<title short\>|Title. Stop at first colon|Text| |\<title short\>|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| |\<author\>|Author(s)|Name List|
|\<first author\>|First author|Text| |\<first author\>|First author|Text|
|\<narrator\>|Narrator(s)|Name List| |\<narrator\>|Narrator(s)|Name List|

View File

@ -35,6 +35,9 @@ namespace ApplicationServices
[Name("Title")] [Name("Title")]
public string Title { get; set; } public string Title { get; set; }
[Name("Subtitle")]
public string Subtitle { get; set; }
[Name("Authors")] [Name("Authors")]
public string AuthorNames { get; set; } public string AuthorNames { get; set; }
@ -123,6 +126,7 @@ namespace ApplicationServices
AudibleProductId = a.Book.AudibleProductId, AudibleProductId = a.Book.AudibleProductId,
Locale = a.Book.Locale, Locale = a.Book.Locale,
Title = a.Book.Title, Title = a.Book.Title,
Subtitle = a.Book.Subtitle,
AuthorNames = a.Book.AuthorNames(), AuthorNames = a.Book.AuthorNames(),
NarratorNames = a.Book.NarratorNames(), NarratorNames = a.Book.NarratorNames(),
LengthInMinutes = a.Book.LengthInMinutes, LengthInMinutes = a.Book.LengthInMinutes,
@ -198,6 +202,7 @@ namespace ApplicationServices
nameof(ExportDto.AudibleProductId), nameof(ExportDto.AudibleProductId),
nameof(ExportDto.Locale), nameof(ExportDto.Locale),
nameof(ExportDto.Title), nameof(ExportDto.Title),
nameof(ExportDto.Subtitle),
nameof(ExportDto.AuthorNames), nameof(ExportDto.AuthorNames),
nameof(ExportDto.NarratorNames), nameof(ExportDto.NarratorNames),
nameof(ExportDto.LengthInMinutes), nameof(ExportDto.LengthInMinutes),
@ -256,6 +261,7 @@ namespace ApplicationServices
row.CreateCell(col++).SetCellValue(dto.AudibleProductId); row.CreateCell(col++).SetCellValue(dto.AudibleProductId);
row.CreateCell(col++).SetCellValue(dto.Locale); row.CreateCell(col++).SetCellValue(dto.Locale);
row.CreateCell(col++).SetCellValue(dto.Title); row.CreateCell(col++).SetCellValue(dto.Title);
row.CreateCell(col++).SetCellValue(dto.Subtitle);
row.CreateCell(col++).SetCellValue(dto.AuthorNames); row.CreateCell(col++).SetCellValue(dto.AuthorNames);
row.CreateCell(col++).SetCellValue(dto.NarratorNames); row.CreateCell(col++).SetCellValue(dto.NarratorNames);
row.CreateCell(col++).SetCellValue(dto.LengthInMinutes); row.CreateCell(col++).SetCellValue(dto.LengthInMinutes);

View File

@ -108,7 +108,7 @@ namespace ApplicationServices
var recordsObj = new JObject var recordsObj = new JObject
{ {
{ "title", libraryBook.Book.Title}, { "title", libraryBook.Book.TitleWithSubtitle},
{ "asin", libraryBook.Book.AudibleProductId}, { "asin", libraryBook.Book.AudibleProductId},
{ "exportTime", DateTime.Now}, { "exportTime", DateTime.Now},
{ "records", JArray.FromObject(recordsEx) } { "records", JArray.FromObject(recordsEx) }

View File

@ -20,6 +20,7 @@ namespace DataLayer.Configurations
entity.Ignore(nameof(Book.Authors)); entity.Ignore(nameof(Book.Authors));
entity.Ignore(nameof(Book.Narrators)); entity.Ignore(nameof(Book.Narrators));
entity.Ignore(nameof(Book.AudioFormat)); entity.Ignore(nameof(Book.AudioFormat));
entity.Ignore(nameof(Book.TitleWithSubtitle));
//// these don't seem to matter //// these don't seem to matter
//entity.Ignore(nameof(Book.AuthorNames)); //entity.Ignore(nameof(Book.AuthorNames));
//entity.Ignore(nameof(Book.NarratorNames)); //entity.Ignore(nameof(Book.NarratorNames));

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Dinah.Core; using Dinah.Core;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -34,8 +33,10 @@ namespace DataLayer
// immutable // immutable
public string AudibleProductId { get; private set; } public string AudibleProductId { get; private set; }
public string Title { get; set; } public string Title { get; private set; }
public string Subtitle { get; 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 string Description { get; private set; }
public int LengthInMinutes { get; private set; } public int LengthInMinutes { get; private set; }
public ContentType ContentType { get; private set; } public ContentType ContentType { get; private set; }
@ -101,8 +102,7 @@ namespace DataLayer
Category = category; Category = category;
// simple assigns // simple assigns
Title = title.Trim() ?? ""; UpdateTitle(title, subtitle);
Subtitle = subtitle?.Trim() ?? "";
Description = description?.Trim() ?? ""; Description = description?.Trim() ?? "";
LengthInMinutes = lengthInMinutes; LengthInMinutes = lengthInMinutes;
ContentType = contentType; ContentType = contentType;
@ -111,6 +111,12 @@ namespace DataLayer
ReplaceAuthors(authors); ReplaceAuthors(authors);
ReplaceNarrators(narrators); ReplaceNarrators(narrators);
} }
public void UpdateTitle(string title, string subtitle)
{
Title = title?.Trim() ?? "";
Subtitle = subtitle?.Trim() ?? "";
_titleWithSubtitle = null;
}
#region contributors, authors, narrators #region contributors, authors, narrators
// use uninitialised backing fields - this means we can detect if the collection was loaded // use uninitialised backing fields - this means we can detect if the collection was loaded
@ -237,6 +243,6 @@ namespace DataLayer
Category = category; Category = category;
} }
public override string ToString() => $"[{AudibleProductId}] {Title}"; public override string ToString() => $"[{AudibleProductId}] {TitleWithSubtitle}";
} }
} }

View File

@ -8,7 +8,7 @@ namespace DataLayer
{ {
public static class EntityExtensions 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 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)); 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); 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}"); var titlesAgg = titles.Take(max).Aggregate((a, b) => $"{a}\r\n{b}");
if (titles.Count == max + 1) if (titles.Count == max + 1)
titlesAgg += $"\r\n\r\nand 1 other"; titlesAgg += $"\r\n\r\nand 1 other";

View File

@ -166,8 +166,7 @@ namespace DtoImporterService
var item = importItem.DtoItem; var item = importItem.DtoItem;
// Update the book titles, since formatting can change // Update the book titles, since formatting can change
book.Title = item.Title; book.UpdateTitle(item.Title, item.Subtitle);
book.Subtitle = item.Subtitle;
var codec = item.AvailableCodecs?.Max(f => AudioFormat.FromString(f.EnhancedCodec)) ?? new AudioFormat(); var codec = item.AvailableCodecs?.Max(f => AudioFormat.FromString(f.EnhancedCodec)) ?? new AudioFormat();
book.AudioFormat = codec; book.AudioFormat = codec;

View File

@ -331,9 +331,9 @@ namespace FileLiberator
string errorTitle() string errorTitle()
{ {
var title var title
= (libraryBook.Book.Title.Length > 53) = (libraryBook.Book.TitleWithSubtitle.Length > 53)
? $"{libraryBook.Book.Title.Truncate(50)}..." ? $"{libraryBook.Book.TitleWithSubtitle.Truncate(50)}..."
: libraryBook.Book.Title; : libraryBook.Book.TitleWithSubtitle;
var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]"; var errorBookTitle = $"{title} [{libraryBook.Book.AudibleProductId}]";
return errorBookTitle; return errorBookTitle;
}; };

View File

@ -45,7 +45,7 @@ namespace FileLiberator
Serilog.Log.Logger.Information("Begin " + nameof(ProcessSingleAsync) + " {@DebugInfo}", new Serilog.Log.Logger.Information("Begin " + nameof(ProcessSingleAsync) + " {@DebugInfo}", new
{ {
libraryBook.Book.Title, libraryBook.Book.TitleWithSubtitle,
libraryBook.Book.AudibleProductId, libraryBook.Book.AudibleProductId,
libraryBook.Book.Locale, libraryBook.Book.Locale,
Account = libraryBook.Account?.ToMask() ?? "[empty]" Account = libraryBook.Account?.ToMask() ?? "[empty]"

View File

@ -14,7 +14,7 @@ namespace FileLiberator
public static (string id, string title, string locale, string account) LogFriendly(this LibraryBook libraryBook) public static (string id, string title, string locale, string account) LogFriendly(this LibraryBook libraryBook)
=> ( => (
id: libraryBook.Book.AudibleProductId, id: libraryBook.Book.AudibleProductId,
title: libraryBook.Book.Title, title: libraryBook.Book.TitleWithSubtitle,
locale: libraryBook.Book.Locale, locale: libraryBook.Book.Locale,
account: libraryBook.Account.ToMask() account: libraryBook.Account.ToMask()
); );
@ -40,8 +40,9 @@ namespace FileLiberator
DateAdded = libraryBook.DateAdded, DateAdded = libraryBook.DateAdded,
AudibleProductId = libraryBook.Book.AudibleProductId, AudibleProductId = libraryBook.Book.AudibleProductId,
Title = libraryBook.Book.Title ?? "", Title = libraryBook.Book.Title,
Subtitle = libraryBook.Book.Subtitle ?? "", Subtitle = libraryBook.Book.Subtitle,
TitleWithSubtitle = libraryBook.Book.TitleWithSubtitle,
Locale = libraryBook.Book.Locale, Locale = libraryBook.Book.Locale,
YearPublished = libraryBook.Book.DatePublished?.Year, YearPublished = libraryBook.Book.DatePublished?.Year,
DatePublished = libraryBook.Book.DatePublished, DatePublished = libraryBook.Book.DatePublished,

View File

@ -21,7 +21,7 @@ namespace LibationAvalonia.Dialogs
set set
{ {
_libraryBook = value; _libraryBook = value;
Title = _libraryBook.Book.Title; Title = _libraryBook.Book.TitleWithSubtitle;
DataContext = _viewModel = new BookDetailsDialogViewModel(_libraryBook); DataContext = _viewModel = new BookDetailsDialogViewModel(_libraryBook);
} }
} }
@ -106,9 +106,11 @@ namespace LibationAvalonia.Dialogs
var picture = PictureStorage.GetPictureSynchronously(new PictureDefinition(libraryBook.Book.PictureId, PictureSize._80x80)); var picture = PictureStorage.GetPictureSynchronously(new PictureDefinition(libraryBook.Book.PictureId, PictureSize._80x80));
Cover = AvaloniaUtils.TryLoadImageOrDefault(picture, 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 //init book details
DetailsText = @$" DetailsText = @$"
Title: {Book.Title} Title: {title}
Author(s): {Book.AuthorNames()} Author(s): {Book.AuthorNames()}
Narrator(s): {Book.NarratorNames()} Narrator(s): {Book.NarratorNames()}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}

View File

@ -37,7 +37,7 @@ namespace LibationAvalonia.Dialogs
public BookRecordsDialog(LibraryBook libraryBook) : this() public BookRecordsDialog(LibraryBook libraryBook) : this()
{ {
this.libraryBook = libraryBook; this.libraryBook = libraryBook;
Title = $"{libraryBook.Book.Title} - Clips and Bookmarks"; Title = $"{libraryBook.Book.TitleWithSubtitle} - Clips and Bookmarks";
Loaded += BookRecordsDialog_Loaded; Loaded += BookRecordsDialog_Loaded;
} }
@ -148,7 +148,7 @@ namespace LibationAvalonia.Dialogs
await Dispatcher.UIThread.InvokeAsync(() => new FilePickerSaveOptions await Dispatcher.UIThread.InvokeAsync(() => new FilePickerSaveOptions
{ {
Title = "Where to export book records", Title = "Where to export book records",
SuggestedFileName = $"{libraryBook.Book.Title} - Records", SuggestedFileName = $"{libraryBook.Book.TitleWithSubtitle} - Records",
DefaultExtension = "xlsx", DefaultExtension = "xlsx",
ShowOverwritePrompt = true, ShowOverwritePrompt = true,
FileTypeChoices = new FilePickerFileType[] FileTypeChoices = new FilePickerFileType[]

View File

@ -105,7 +105,7 @@ namespace LibationAvalonia.ViewModels
LibraryBook = libraryBook; LibraryBook = libraryBook;
Logger = logme; Logger = logme;
_title = LibraryBook.Book.Title; _title = LibraryBook.Book.TitleWithSubtitle;
_author = LibraryBook.Book.AuthorNames(); _author = LibraryBook.Book.AuthorNames();
_narrator = LibraryBook.Book.NarratorNames(); _narrator = LibraryBook.Book.NarratorNames();
@ -305,7 +305,7 @@ namespace LibationAvalonia.ViewModels
Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}"); Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}");
Title = libraryBook.Book.Title; Title = libraryBook.Book.TitleWithSubtitle;
Author = libraryBook.Book.AuthorNames(); Author = libraryBook.Book.AuthorNames();
Narrator = libraryBook.Book.NarratorNames(); Narrator = libraryBook.Book.NarratorNames();
} }
@ -372,7 +372,7 @@ namespace LibationAvalonia.ViewModels
: str; : str;
details = details =
$@" Title: {libraryBook.Book.Title} $@" Title: {libraryBook.Book.TitleWithSubtitle}
ID: {libraryBook.Book.AudibleProductId} ID: {libraryBook.Book.AudibleProductId}
Author: {trunc(libraryBook.Book.AuthorNames())} Author: {trunc(libraryBook.Book.AuthorNames())}
Narr: {trunc(libraryBook.Book.NarratorNames())}"; Narr: {trunc(libraryBook.Book.NarratorNames())}";
@ -392,7 +392,7 @@ $@" Title: {libraryBook.Book.Title}
{ {
libraryBook.UpdateBookStatus(LiberatedStatus.Error); 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; return ProcessBookResult.FailedSkip;
} }

View File

@ -227,7 +227,7 @@ namespace LibationAvalonia.ViewModels
else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage) else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage)
{ {
await MessageBox.Show(@$" 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. 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.
", ",

View File

@ -159,7 +159,7 @@ namespace LibationAvalonia.Views
var openFileDialogOptions = new FilePickerOpenOptions var openFileDialogOptions = new FilePickerOpenOptions
{ {
Title = $"Locate the audio file for '{entry.Book.Title}'", Title = $"Locate the audio file for '{entry.Book.TitleWithSubtitle}'",
AllowMultiple = false, AllowMultiple = false,
SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix), SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix),
FileTypeFilter = new FilePickerFileType[] FileTypeFilter = new FilePickerFileType[]

View File

@ -9,20 +9,7 @@ namespace LibationFileManager
public string AudibleProductId { get; set; } public string AudibleProductId { get; set; }
public string Title { get; set; } public string Title { get; set; }
public string Subtitle { get; set; } public string Subtitle { get; set; }
public string TitleWithSubtitle public string TitleWithSubtitle { get; set; }
{
get
{
string text = Title?.Trim();
string text2 = Subtitle?.Trim();
if (string.IsNullOrWhiteSpace(text2))
{
return text;
}
return text + ": " + text2;
}
}
public string Locale { get; set; } public string Locale { get; set; }
public int? YearPublished { get; set; } public int? YearPublished { get; set; }

View File

@ -24,8 +24,10 @@ namespace LibationFileManager
public static TemplateTags ChNumber0 { get; } = new TemplateTags("ch# 0", "Chapter # with leading zeros"); 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 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 with subtitle");
public static TemplateTags TitleShort { get; } = new TemplateTags("title short", "Title. Stop at first colon"); 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 Author { get; } = new TemplateTags("author", "Author(s)");
public static TemplateTags FirstAuthor { get; } = new TemplateTags("first author", "First author"); public static TemplateTags FirstAuthor { get; } = new TemplateTags("first author", "First author");
public static TemplateTags Narrator { get; } = new TemplateTags("narrator", "Narrator(s)"); public static TemplateTags Narrator { get; } = new TemplateTags("narrator", "Narrator(s)");

View File

@ -248,7 +248,9 @@ namespace LibationFileManager
//Don't allow formatting of Id //Don't allow formatting of Id
{ TemplateTags.Id, lb => lb.AudibleProductId, v => v }, { TemplateTags.Id, lb => lb.AudibleProductId, v => v },
{ TemplateTags.Title, lb => lb.TitleWithSubtitle }, { TemplateTags.Title, lb => lb.TitleWithSubtitle },
{ TemplateTags.TitleShort, lb => lb.Title }, { TemplateTags.TitleShort, lb => getTitleShort(lb.Title) },
{ TemplateTags.AudibleTitle, lb => lb.Title },
{ TemplateTags.AudibleSubtitle, lb => lb.Subtitle },
{ TemplateTags.Author, lb => lb.Authors, NameListFormat.Formatter }, { TemplateTags.Author, lb => lb.Authors, NameListFormat.Formatter },
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor }, { TemplateTags.FirstAuthor, lb => lb.FirstAuthor },
{ TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter }, { TemplateTags.Narrator, lb => lb.Narrators, NameListFormat.Formatter },
@ -275,7 +277,9 @@ namespace LibationFileManager
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter) new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
{ {
{ TemplateTags.Title, lb => lb.TitleWithSubtitle }, { TemplateTags.Title, lb => lb.TitleWithSubtitle },
{ TemplateTags.TitleShort, lb => lb.Title }, { 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.SeriesName },
}, },
new PropertyTagCollection<MultiConvertFileProperties>(caseSensative: true, StringFormatter, IntegerFormatter, DateTimeFormatter) new PropertyTagCollection<MultiConvertFileProperties>(caseSensative: true, StringFormatter, IntegerFormatter, DateTimeFormatter)

View File

@ -35,7 +35,7 @@ namespace LibationSearchEngine
{ {
{ FieldType.ID, lb => lb.Book.AudibleProductId.ToLowerInvariant(), nameof(Book.AudibleProductId), "ProductId", "Id", "ASIN" }, { FieldType.ID, lb => lb.Book.AudibleProductId.ToLowerInvariant(), nameof(Book.AudibleProductId), "ProductId", "Id", "ASIN" },
{ FieldType.Raw, lb => lb.Book.AudibleProductId, _ID_ }, { 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.AuthorNames(), "AuthorNames", "Author", "Authors" },
{ FieldType.String, lb => lb.Book.NarratorNames(), "NarratorNames", "Narrator", "Narrators" }, { FieldType.String, lb => lb.Book.NarratorNames(), "NarratorNames", "Narrator", "Narrators" },
{ FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) }, { FieldType.String, lb => lb.Book.Publisher, nameof(Book.Publisher) },

View File

@ -105,7 +105,7 @@ namespace LibationUiBase.GridView
Liberate = TStatus.Create(libraryBook); Liberate = TStatus.Create(libraryBook);
Liberate.Expanded = expanded; Liberate.Expanded = expanded;
Title = Book.Title; Title = Book.TitleWithSubtitle;
Series = Book.SeriesNames(includeIndex: true); Series = Book.SeriesNames(includeIndex: true);
SeriesOrder = new SeriesOrder(Book.SeriesLink); SeriesOrder = new SeriesOrder(Book.SeriesLink);
Length = GetBookLengthString(); Length = GetBookLengthString();

View File

@ -108,7 +108,7 @@ namespace LibationUiBase.SeriesView
{ {
Asin = seriesParent.AudibleProductId, Asin = seriesParent.AudibleProductId,
Sequence = item.Relationships.FirstOrDefault(r => r.Asin == seriesParent.AudibleProductId)?.Sort?.ToString() ?? "0", Sequence = item.Relationships.FirstOrDefault(r => r.Asin == seriesParent.AudibleProductId)?.Sort?.ToString() ?? "0",
Title = seriesParent.Title Title = seriesParent.TitleWithSubtitle
} }
}; };
} }

View File

@ -38,13 +38,14 @@ namespace LibationWinForms.Dialogs
// 1st draft: lazily cribbed from GridEntry.ctor() // 1st draft: lazily cribbed from GridEntry.ctor()
private void initDetails() private void initDetails()
{ {
this.Text = Book.Title; this.Text = Book.TitleWithSubtitle;
(_, var picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80)); (_, var picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80));
this.coverPb.Image = WinFormsUtil.TryLoadImageOrDefault(picture, 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 = @$" var t = @$"
Title: {Book.Title} Title: {title}
Author(s): {Book.AuthorNames()} Author(s): {Book.AuthorNames()}
Narrator(s): {Book.NarratorNames()} Narrator(s): {Book.NarratorNames()}
Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")} Length: {(Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min")}

View File

@ -45,7 +45,7 @@ namespace LibationWinForms.Dialogs
{ {
this.libraryBook = libraryBook; 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) private async void BookRecordsDialog_Shown(object sender, EventArgs e)
@ -182,7 +182,7 @@ namespace LibationWinForms.Dialogs
{ {
Title = "Where to export records", Title = "Where to export records",
AddExtension = true, AddExtension = true,
FileName = $"{libraryBook.Book.Title} - Records", FileName = $"{libraryBook.Book.TitleWithSubtitle} - Records",
DefaultExt = "xlsx", DefaultExt = "xlsx",
Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*" Filter = "Excel Workbook (*.xlsx)|*.xlsx|CSV files (*.csv)|*.csv|JSON files (*.json)|*.json" // + "|All files (*.*)|*.*"
}); });

View File

@ -176,7 +176,7 @@ namespace LibationWinForms.GridView
{ {
var openFileDialog = new OpenFileDialog 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 (*.*)|*.*", Filter = "All files (*.*)|*.*",
FilterIndex = 1 FilterIndex = 1
}; };

View File

@ -77,7 +77,7 @@ namespace LibationWinForms.ProcessQueue
LibraryBook = libraryBook; LibraryBook = libraryBook;
Logger = logme; Logger = logme;
title = LibraryBook.Book.Title; title = LibraryBook.Book.TitleWithSubtitle;
authorNames = LibraryBook.Book.AuthorNames(); authorNames = LibraryBook.Book.AuthorNames();
narratorNames = LibraryBook.Book.NarratorNames(); narratorNames = LibraryBook.Book.NarratorNames();
_bookText = $"{title}\r\nBy {authorNames}\r\nNarrated by {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}"); Logger.Info($"{Environment.NewLine}{((Processable)sender).Name} Step, Begin: {libraryBook.Book}");
title = libraryBook.Book.Title; title = libraryBook.Book.TitleWithSubtitle;
authorNames = libraryBook.Book.AuthorNames(); authorNames = libraryBook.Book.AuthorNames();
narratorNames = libraryBook.Book.NarratorNames(); narratorNames = libraryBook.Book.NarratorNames();
updateBookInfo(); updateBookInfo();
@ -359,7 +359,7 @@ namespace LibationWinForms.ProcessQueue
: str; : str;
details = details =
$@" Title: {libraryBook.Book.Title} $@" Title: {libraryBook.Book.TitleWithSubtitle}
ID: {libraryBook.Book.AudibleProductId} ID: {libraryBook.Book.AudibleProductId}
Author: {trunc(libraryBook.Book.AuthorNames())} Author: {trunc(libraryBook.Book.AuthorNames())}
Narr: {trunc(libraryBook.Book.NarratorNames())}"; Narr: {trunc(libraryBook.Book.NarratorNames())}";
@ -379,7 +379,7 @@ $@" Title: {libraryBook.Book.Title}
{ {
libraryBook.UpdateBookStatus(LiberatedStatus.Error); 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; return ProcessBookResult.FailedSkip;
} }

View File

@ -188,7 +188,7 @@ namespace LibationWinForms.ProcessQueue
else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage) else if (result == ProcessBookResult.LicenseDeniedPossibleOutage && !shownServiceOutageMessage)
{ {
MessageBox.Show(@$" 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. 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.
", ",

View File

@ -463,7 +463,7 @@ namespace Templates_Other
extension = FileUtility.GetStandardizedExtension(extension); extension = FileUtility.GetStandardizedExtension(extension);
var lbDto = GetLibraryBook(); var lbDto = GetLibraryBook();
lbDto.Title = title; lbDto.TitleWithSubtitle = title;
lbDto.AudibleProductId = "ID123456"; lbDto.AudibleProductId = "ID123456";
Templates.TryGetTemplate<Templates.FolderTemplate>(template, out var fileNamingTemplate).Should().BeTrue(); 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 template = Path.GetFileNameWithoutExtension(originalPath) + " - <ch# 0> - <title>" + estension;
var lbDto = GetLibraryBook(); var lbDto = GetLibraryBook();
lbDto.Title = suffix; lbDto.TitleWithSubtitle = suffix;
Templates.TryGetTemplate<Templates.ChapterFileTemplate>(template, out var chapterFileTemplate).Should().BeTrue(); Templates.TryGetTemplate<Templates.ChapterFileTemplate>(template, out var chapterFileTemplate).Should().BeTrue();
@ -508,7 +508,7 @@ namespace Templates_Other
if (Environment.OSVersion.Platform == platformID) if (Environment.OSVersion.Platform == platformID)
{ {
var lbDto = GetLibraryBook(); 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 directory = Path.GetDirectoryName(inStr);
var fileName = Path.GetFileName(inStr); var fileName = Path.GetFileName(inStr);