Integrate new Title and Subtitle properties into Libation
This commit is contained in:
parent
2195574422
commit
83fa73cef5
@ -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|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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) }
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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";
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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]"
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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")}
|
||||||
|
|||||||
@ -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[]
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.
|
||||||
",
|
",
|
||||||
|
|||||||
@ -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[]
|
||||||
|
|||||||
@ -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; }
|
||||||
|
|
||||||
|
|||||||
@ -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)");
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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) },
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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")}
|
||||||
|
|||||||
@ -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 (*.*)|*.*"
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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.
|
||||||
",
|
",
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user