feat(database): add Book and Audiobook entities (#122)

Add entities for books and audiobooks with:
- Book entity: title, ISBN, ASIN, publisher, author/series links
- Audiobook entity: title, narrator, duration, abridged flag, book link
- Database migration 246 for Books and Audiobooks tables
- Entity registrations in TableMapping

Note: Depends on PR #118 (Author/Series tables) for full FK support.

Co-authored-by: admin <admin@ardentleatherworks.com>
This commit is contained in:
Cody Kickertz 2025-12-21 19:51:44 -06:00 committed by GitHub
parent b4009132d1
commit b7cd7b20e8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 167 additions and 0 deletions

View file

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaTypes;
namespace NzbDrone.Core.Audiobooks
{
public class Audiobook : ModelBase
{
public Audiobook()
{
Tags = new HashSet<int>();
MediaType = MediaType.Audiobook;
}
public string Title { get; set; }
public string SortTitle { get; set; }
public string Description { get; set; }
public string ForeignAudiobookId { get; set; }
public string Isbn { get; set; }
public string Isbn13 { get; set; }
public string Asin { get; set; }
public DateTime? ReleaseDate { get; set; }
public string Publisher { get; set; }
public string Language { get; set; }
public string Narrator { get; set; }
public int? DurationMinutes { get; set; }
public bool IsAbridged { get; set; }
public MediaType MediaType { get; set; }
public bool Monitored { get; set; }
public int QualityProfileId { get; set; }
public string Path { get; set; }
public string RootFolderPath { get; set; }
public DateTime Added { get; set; }
public HashSet<int> Tags { get; set; }
public DateTime? LastSearchTime { get; set; }
public int? AuthorId { get; set; }
public int? SeriesId { get; set; }
public int? SeriesPosition { get; set; }
public int? BookId { get; set; }
public override string ToString()
{
var narratorInfo = string.IsNullOrEmpty(Narrator) ? "" : $" - Narrated by {Narrator}";
return $"{Title} ({ReleaseDate?.Year}){narratorInfo}";
}
}
}

View file

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.MediaTypes;
namespace NzbDrone.Core.Books
{
public class Book : ModelBase
{
public Book()
{
Tags = new HashSet<int>();
MediaType = MediaType.Book;
}
public string Title { get; set; }
public string SortTitle { get; set; }
public string Description { get; set; }
public string ForeignBookId { get; set; }
public string Isbn { get; set; }
public string Isbn13 { get; set; }
public string Asin { get; set; }
public int? PageCount { get; set; }
public DateTime? ReleaseDate { get; set; }
public string Publisher { get; set; }
public string Language { get; set; }
public MediaType MediaType { get; set; }
public bool Monitored { get; set; }
public int QualityProfileId { get; set; }
public string Path { get; set; }
public string RootFolderPath { get; set; }
public DateTime Added { get; set; }
public HashSet<int> Tags { get; set; }
public DateTime? LastSearchTime { get; set; }
public int? AuthorId { get; set; }
public int? SeriesId { get; set; }
public int? SeriesPosition { get; set; }
public override string ToString()
{
return $"{Title} ({ReleaseDate?.Year})";
}
}
}

View file

@ -0,0 +1,63 @@
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(246)]
public class add_books_audiobooks_tables : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("Books")
.WithColumn("Title").AsString().NotNullable()
.WithColumn("SortTitle").AsString().Nullable()
.WithColumn("Description").AsString().Nullable()
.WithColumn("ForeignBookId").AsString().Nullable()
.WithColumn("Isbn").AsString().Nullable()
.WithColumn("Isbn13").AsString().Nullable()
.WithColumn("Asin").AsString().Nullable()
.WithColumn("PageCount").AsInt32().Nullable()
.WithColumn("ReleaseDate").AsDateTime().Nullable()
.WithColumn("Publisher").AsString().Nullable()
.WithColumn("Language").AsString().Nullable()
.WithColumn("MediaType").AsInt32().WithDefaultValue(4)
.WithColumn("Monitored").AsBoolean().WithDefaultValue(false)
.WithColumn("QualityProfileId").AsInt32().WithDefaultValue(0)
.WithColumn("Path").AsString().Nullable()
.WithColumn("RootFolderPath").AsString().Nullable()
.WithColumn("Added").AsDateTime().WithDefaultValue(System.DateTime.UtcNow)
.WithColumn("Tags").AsString().WithDefaultValue("[]")
.WithColumn("LastSearchTime").AsDateTime().Nullable()
.WithColumn("AuthorId").AsInt32().Nullable()
.WithColumn("SeriesId").AsInt32().Nullable()
.WithColumn("SeriesPosition").AsInt32().Nullable();
Create.TableForModel("Audiobooks")
.WithColumn("Title").AsString().NotNullable()
.WithColumn("SortTitle").AsString().Nullable()
.WithColumn("Description").AsString().Nullable()
.WithColumn("ForeignAudiobookId").AsString().Nullable()
.WithColumn("Isbn").AsString().Nullable()
.WithColumn("Isbn13").AsString().Nullable()
.WithColumn("Asin").AsString().Nullable()
.WithColumn("ReleaseDate").AsDateTime().Nullable()
.WithColumn("Publisher").AsString().Nullable()
.WithColumn("Language").AsString().Nullable()
.WithColumn("Narrator").AsString().Nullable()
.WithColumn("DurationMinutes").AsInt32().Nullable()
.WithColumn("IsAbridged").AsBoolean().WithDefaultValue(false)
.WithColumn("MediaType").AsInt32().WithDefaultValue(5)
.WithColumn("Monitored").AsBoolean().WithDefaultValue(false)
.WithColumn("QualityProfileId").AsInt32().WithDefaultValue(0)
.WithColumn("Path").AsString().Nullable()
.WithColumn("RootFolderPath").AsString().Nullable()
.WithColumn("Added").AsDateTime().WithDefaultValue(System.DateTime.UtcNow)
.WithColumn("Tags").AsString().WithDefaultValue("[]")
.WithColumn("LastSearchTime").AsDateTime().Nullable()
.WithColumn("AuthorId").AsInt32().Nullable()
.WithColumn("SeriesId").AsInt32().Nullable()
.WithColumn("SeriesPosition").AsInt32().Nullable()
.WithColumn("BookId").AsInt32().Nullable();
}
}
}

View file

@ -3,10 +3,12 @@
using System.Linq;
using Dapper;
using NzbDrone.Common.Reflection;
using NzbDrone.Core.Audiobooks;
using NzbDrone.Core.Authentication;
using NzbDrone.Core.Authors;
using NzbDrone.Core.AutoTagging.Specifications;
using NzbDrone.Core.Blocklisting;
using NzbDrone.Core.Books;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFilters;
using NzbDrone.Core.CustomFormats;
@ -142,6 +144,10 @@ public static void Map()
Mapper.Entity<ImportListExclusion>("ImportExclusions").RegisterModel();
Mapper.Entity<Book>("Books").RegisterModel();
Mapper.Entity<Audiobook>("Audiobooks").RegisterModel();
Mapper.Entity<QualityDefinition>("QualityDefinitions").RegisterModel()
.Ignore(d => d.GroupName)
.Ignore(d => d.Weight);