feat: add Book subtitle capturing so TitleShort reflects titles better

This commit is contained in:
Alanoll 2023-06-26 12:18:15 -05:00
parent 74ce408c8b
commit 2195574422
9 changed files with 482 additions and 9 deletions

View File

@ -1,5 +1,6 @@
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;
@ -33,7 +34,8 @@ namespace DataLayer
// immutable // immutable
public string AudibleProductId { get; private set; } public string AudibleProductId { get; private set; }
public string Title { get; private set; } public string Title { get; set; }
public string Subtitle { get; set; }
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; }
@ -70,6 +72,7 @@ namespace DataLayer
public Book( public Book(
AudibleProductId audibleProductId, AudibleProductId audibleProductId,
string title, string title,
string subtitle,
string description, string description,
int lengthInMinutes, int lengthInMinutes,
ContentType contentType, ContentType contentType,
@ -99,6 +102,7 @@ namespace DataLayer
// simple assigns // simple assigns
Title = title.Trim() ?? ""; Title = title.Trim() ?? "";
Subtitle = subtitle?.Trim() ?? "";
Description = description?.Trim() ?? ""; Description = description?.Trim() ?? "";
LengthInMinutes = lengthInMinutes; LengthInMinutes = lengthInMinutes;
ContentType = contentType; ContentType = contentType;

View File

@ -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
}
}
}

View File

@ -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");
}
}
}

View File

@ -15,7 +15,7 @@ namespace DataLayer.Migrations
protected override void BuildModel(ModelBuilder modelBuilder) protected override void BuildModel(ModelBuilder modelBuilder)
{ {
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "7.0.3"); modelBuilder.HasAnnotation("ProductVersion", "7.0.5");
modelBuilder.Entity("DataLayer.Book", b => modelBuilder.Entity("DataLayer.Book", b =>
{ {
@ -56,6 +56,9 @@ namespace DataLayer.Migrations
b.Property<string>("PictureLarge") b.Property<string>("PictureLarge")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Subtitle")
.HasColumnType("TEXT");
b.Property<string>("Title") b.Property<string>("Title")
.HasColumnType("TEXT"); .HasColumnType("TEXT");

View File

@ -118,7 +118,8 @@ namespace DtoImporterService
{ {
book = DbContext.Books.Add(new Book( book = DbContext.Books.Add(new Book(
new AudibleProductId(item.ProductId), new AudibleProductId(item.ProductId),
item.TitleWithSubtitle, item.Title,
item.Subtitle,
item.Description, item.Description,
item.LengthInMinutes, item.LengthInMinutes,
contentType, contentType,
@ -164,6 +165,10 @@ namespace DtoImporterService
{ {
var item = importItem.DtoItem; var item = importItem.DtoItem;
// Update the book titles, since formatting can change
book.Title = item.Title;
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

@ -41,6 +41,7 @@ namespace FileLiberator
AudibleProductId = libraryBook.Book.AudibleProductId, AudibleProductId = libraryBook.Book.AudibleProductId,
Title = libraryBook.Book.Title ?? "", Title = libraryBook.Book.Title ?? "",
Subtitle = libraryBook.Book.Subtitle ?? "",
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

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

View File

@ -57,7 +57,8 @@ namespace LibationFileManager
DateAdded = new DateTime(2022, 6, 9, 0, 0, 0), DateAdded = new DateTime(2022, 6, 9, 0, 0, 0),
DatePublished = new DateTime(2017, 2, 27, 0, 0, 0), DatePublished = new DateTime(2017, 2, 27, 0, 0, 0),
AudibleProductId = "123456789", AudibleProductId = "123456789",
Title = "A Study in Scarlet: A Sherlock Holmes Novel", Title = "A Study in Scarlet",
Subtitle = "A Sherlock Holmes Novel",
Locale = "us", Locale = "us",
YearPublished = 2017, YearPublished = 2017,
Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" }, Authors = new List<string> { "Arthur Conan Doyle", "Stephen Fry - introductions" },

View File

@ -247,8 +247,8 @@ 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.Title }, { TemplateTags.Title, lb => lb.TitleWithSubtitle },
{ TemplateTags.TitleShort, lb => getTitleShort(lb.Title) }, { TemplateTags.TitleShort, lb => lb.Title },
{ 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 },
@ -274,8 +274,8 @@ namespace LibationFileManager
{ {
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter) new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
{ {
{ TemplateTags.Title, lb => lb.Title }, { TemplateTags.Title, lb => lb.TitleWithSubtitle },
{ TemplateTags.TitleShort, lb => getTitleShort(lb.Title) }, { TemplateTags.TitleShort, lb => lb.Title },
{ 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)