Logging Enhancements (#1521)

* Recreated Kavita Logging with Serilog instead of Default. This needs to be move out of the appsettings now, to allow auto updater to patch.

* Refactored the code to be completely configured via Code rather than appsettings.json. This is a required step for Auto Updating.

* Added in the ability to send logs directly to the UI only for users on the log route. Stopping implementation as Alerts page will handle the rest of the implementation.

* Fixed up the backup service to not rely on Config from appsettings.json

* Tweaked the Logging levels available

* Moved everything over to File-scoped namespaces

* Moved everything over to File-scoped namespaces

* Code cleanup, removed an old migration and changed so debug logging doesn't print sensitive db data

* Removed dead code
This commit is contained in:
Joseph Milazzo 2022-09-12 19:25:48 -05:00 committed by GitHub
parent 9f715cc35f
commit d1a14f7e68
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
212 changed files with 16599 additions and 16834 deletions

View file

@ -1,10 +1,9 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;
namespace API.Entities
namespace API.Entities;
public class AppRole : IdentityRole<int>
{
public class AppRole : IdentityRole<int>
{
public ICollection<AppUserRole> UserRoles { get; set; }
}
}
public ICollection<AppUserRole> UserRoles { get; set; }
}

View file

@ -5,45 +5,44 @@ using API.Entities.Interfaces;
using Microsoft.AspNetCore.Identity;
namespace API.Entities
namespace API.Entities;
public class AppUser : IdentityUser<int>, IHasConcurrencyToken
{
public class AppUser : IdentityUser<int>, IHasConcurrencyToken
public DateTime Created { get; set; } = DateTime.Now;
public DateTime LastActive { get; set; }
public ICollection<Library> Libraries { get; set; }
public ICollection<AppUserRole> UserRoles { get; set; }
public ICollection<AppUserProgress> Progresses { get; set; }
public ICollection<AppUserRating> Ratings { get; set; }
public AppUserPreferences UserPreferences { get; set; }
public ICollection<AppUserBookmark> Bookmarks { get; set; }
/// <summary>
/// Reading lists associated with this user
/// </summary>
public ICollection<ReadingList> ReadingLists { get; set; }
/// <summary>
/// A list of Series the user want's to read
/// </summary>
public ICollection<Series> WantToRead { get; set; }
/// <summary>
/// An API Key to interact with external services, like OPDS
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// The confirmation token for the user (invite). This will be set to null after the user confirms.
/// </summary>
public string ConfirmationToken { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
public DateTime Created { get; set; } = DateTime.Now;
public DateTime LastActive { get; set; }
public ICollection<Library> Libraries { get; set; }
public ICollection<AppUserRole> UserRoles { get; set; }
public ICollection<AppUserProgress> Progresses { get; set; }
public ICollection<AppUserRating> Ratings { get; set; }
public AppUserPreferences UserPreferences { get; set; }
public ICollection<AppUserBookmark> Bookmarks { get; set; }
/// <summary>
/// Reading lists associated with this user
/// </summary>
public ICollection<ReadingList> ReadingLists { get; set; }
/// <summary>
/// A list of Series the user want's to read
/// </summary>
public ICollection<Series> WantToRead { get; set; }
/// <summary>
/// An API Key to interact with external services, like OPDS
/// </summary>
public string ApiKey { get; set; }
/// <summary>
/// The confirmation token for the user (invite). This will be set to null after the user confirms.
/// </summary>
public string ConfirmationToken { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
RowVersion++;
}
}

View file

@ -2,30 +2,29 @@
using System.Text.Json.Serialization;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
/// <summary>
/// Represents a saved page in a Chapter entity for a given user.
/// </summary>
public class AppUserBookmark : IEntityDate
{
public int Id { get; set; }
public int Page { get; set; }
public int VolumeId { get; set; }
public int SeriesId { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Represents a saved page in a Chapter entity for a given user.
/// Filename in the Bookmark Directory
/// </summary>
public class AppUserBookmark : IEntityDate
{
public int Id { get; set; }
public int Page { get; set; }
public int VolumeId { get; set; }
public int SeriesId { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Filename in the Bookmark Directory
/// </summary>
public string FileName { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
// Relationships
[JsonIgnore]
public AppUser AppUser { get; set; }
public int AppUserId { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
}
// Relationships
[JsonIgnore]
public AppUser AppUser { get; set; }
public int AppUserId { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
}

View file

@ -1,109 +1,108 @@
using API.Entities.Enums;
using API.Entities.Enums.UserPreferences;
namespace API.Entities
namespace API.Entities;
public class AppUserPreferences
{
public class AppUserPreferences
{
public int Id { get; set; }
/// <summary>
/// Manga Reader Option: What direction should the next/prev page buttons go
/// </summary>
public ReadingDirection ReadingDirection { get; set; } = ReadingDirection.LeftToRight;
/// <summary>
/// Manga Reader Option: How should the image be scaled to screen
/// </summary>
public ScalingOption ScalingOption { get; set; } = ScalingOption.Automatic;
/// <summary>
/// Manga Reader Option: Which side of a split image should we show first
/// </summary>
public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.FitSplit;
/// <summary>
/// Manga Reader Option: How the manga reader should perform paging or reading of the file
/// <example>
/// Webtoon uses scrolling to page, MANGA_LR uses paging by clicking left/right side of reader, MANGA_UD uses paging
/// by clicking top/bottom sides of reader.
/// </example>
/// </summary>
public ReaderMode ReaderMode { get; set; }
public int Id { get; set; }
/// <summary>
/// Manga Reader Option: What direction should the next/prev page buttons go
/// </summary>
public ReadingDirection ReadingDirection { get; set; } = ReadingDirection.LeftToRight;
/// <summary>
/// Manga Reader Option: How should the image be scaled to screen
/// </summary>
public ScalingOption ScalingOption { get; set; } = ScalingOption.Automatic;
/// <summary>
/// Manga Reader Option: Which side of a split image should we show first
/// </summary>
public PageSplitOption PageSplitOption { get; set; } = PageSplitOption.FitSplit;
/// <summary>
/// Manga Reader Option: How the manga reader should perform paging or reading of the file
/// <example>
/// Webtoon uses scrolling to page, MANGA_LR uses paging by clicking left/right side of reader, MANGA_UD uses paging
/// by clicking top/bottom sides of reader.
/// </example>
/// </summary>
public ReaderMode ReaderMode { get; set; }
/// <summary>
/// Manga Reader Option: Allow the menu to close after 6 seconds without interaction
/// </summary>
public bool AutoCloseMenu { get; set; } = true;
/// <summary>
/// Manga Reader Option: Show screen hints to the user on some actions, ie) pagination direction change
/// </summary>
public bool ShowScreenHints { get; set; } = true;
/// <summary>
/// Manga Reader Option: How many pages to display in the reader at once
/// </summary>
public LayoutMode LayoutMode { get; set; } = LayoutMode.Single;
/// <summary>
/// Manga Reader Option: Background color of the reader
/// </summary>
public string BackgroundColor { get; set; } = "#000000";
/// <summary>
/// Book Reader Option: Override extra Margin
/// </summary>
public int BookReaderMargin { get; set; } = 15;
/// <summary>
/// Book Reader Option: Override line-height
/// </summary>
public int BookReaderLineSpacing { get; set; } = 100;
/// <summary>
/// Book Reader Option: Override font size
/// </summary>
public int BookReaderFontSize { get; set; } = 100;
/// <summary>
/// Book Reader Option: Maps to the default Kavita font-family (inherit) or an override
/// </summary>
public string BookReaderFontFamily { get; set; } = "default";
/// <summary>
/// Book Reader Option: Allows tapping on side of screens to paginate
/// </summary>
public bool BookReaderTapToPaginate { get; set; } = false;
/// <summary>
/// Book Reader Option: What direction should the next/prev page buttons go
/// </summary>
public ReadingDirection BookReaderReadingDirection { get; set; } = ReadingDirection.LeftToRight;
/// <summary>
/// UI Site Global Setting: The UI theme the user should use.
/// </summary>
/// <remarks>Should default to Dark</remarks>
public SiteTheme Theme { get; set; }
/// <summary>
/// Book Reader Option: The color theme to decorate the book contents
/// </summary>
/// <remarks>Should default to Dark</remarks>
public string BookThemeName { get; set; } = "Dark";
/// <summary>
/// Book Reader Option: The way a page from a book is rendered. Default is as book dictates, 1 column is fit to height,
/// 2 column is fit to height, 2 columns
/// </summary>
/// <remarks>Defaults to Default</remarks>
public BookPageLayoutMode BookReaderLayoutMode { get; set; } = BookPageLayoutMode.Default;
/// <summary>
/// Book Reader Option: A flag that hides the menu-ing system behind a click on the screen. This should be used with tap to paginate, but the app doesn't enforce this.
/// </summary>
/// <remarks>Defaults to false</remarks>
public bool BookReaderImmersiveMode { get; set; } = false;
/// <summary>
/// Global Site Option: If the UI should layout items as Cards or List items
/// </summary>
/// <remarks>Defaults to Cards</remarks>
public PageLayoutMode GlobalPageLayoutMode { get; set; } = PageLayoutMode.Cards;
/// <summary>
/// UI Site Global Setting: If unread summaries should be blurred until expanded or unless user has read it already
/// </summary>
/// <remarks>Defaults to false</remarks>
public bool BlurUnreadSummaries { get; set; } = false;
/// <summary>
/// UI Site Global Setting: Should Kavita prompt user to confirm downloads that are greater than 100 MB.
/// </summary>
public bool PromptForDownloadSize { get; set; } = true;
/// <summary>
/// Manga Reader Option: Allow the menu to close after 6 seconds without interaction
/// </summary>
public bool AutoCloseMenu { get; set; } = true;
/// <summary>
/// Manga Reader Option: Show screen hints to the user on some actions, ie) pagination direction change
/// </summary>
public bool ShowScreenHints { get; set; } = true;
/// <summary>
/// Manga Reader Option: How many pages to display in the reader at once
/// </summary>
public LayoutMode LayoutMode { get; set; } = LayoutMode.Single;
/// <summary>
/// Manga Reader Option: Background color of the reader
/// </summary>
public string BackgroundColor { get; set; } = "#000000";
/// <summary>
/// Book Reader Option: Override extra Margin
/// </summary>
public int BookReaderMargin { get; set; } = 15;
/// <summary>
/// Book Reader Option: Override line-height
/// </summary>
public int BookReaderLineSpacing { get; set; } = 100;
/// <summary>
/// Book Reader Option: Override font size
/// </summary>
public int BookReaderFontSize { get; set; } = 100;
/// <summary>
/// Book Reader Option: Maps to the default Kavita font-family (inherit) or an override
/// </summary>
public string BookReaderFontFamily { get; set; } = "default";
/// <summary>
/// Book Reader Option: Allows tapping on side of screens to paginate
/// </summary>
public bool BookReaderTapToPaginate { get; set; } = false;
/// <summary>
/// Book Reader Option: What direction should the next/prev page buttons go
/// </summary>
public ReadingDirection BookReaderReadingDirection { get; set; } = ReadingDirection.LeftToRight;
/// <summary>
/// UI Site Global Setting: The UI theme the user should use.
/// </summary>
/// <remarks>Should default to Dark</remarks>
public SiteTheme Theme { get; set; }
/// <summary>
/// Book Reader Option: The color theme to decorate the book contents
/// </summary>
/// <remarks>Should default to Dark</remarks>
public string BookThemeName { get; set; } = "Dark";
/// <summary>
/// Book Reader Option: The way a page from a book is rendered. Default is as book dictates, 1 column is fit to height,
/// 2 column is fit to height, 2 columns
/// </summary>
/// <remarks>Defaults to Default</remarks>
public BookPageLayoutMode BookReaderLayoutMode { get; set; } = BookPageLayoutMode.Default;
/// <summary>
/// Book Reader Option: A flag that hides the menu-ing system behind a click on the screen. This should be used with tap to paginate, but the app doesn't enforce this.
/// </summary>
/// <remarks>Defaults to false</remarks>
public bool BookReaderImmersiveMode { get; set; } = false;
/// <summary>
/// Global Site Option: If the UI should layout items as Cards or List items
/// </summary>
/// <remarks>Defaults to Cards</remarks>
public PageLayoutMode GlobalPageLayoutMode { get; set; } = PageLayoutMode.Cards;
/// <summary>
/// UI Site Global Setting: If unread summaries should be blurred until expanded or unless user has read it already
/// </summary>
/// <remarks>Defaults to false</remarks>
public bool BlurUnreadSummaries { get; set; } = false;
/// <summary>
/// UI Site Global Setting: Should Kavita prompt user to confirm downloads that are greater than 100 MB.
/// </summary>
public bool PromptForDownloadSize { get; set; } = true;
public AppUser AppUser { get; set; }
public int AppUserId { get; set; }
}
public AppUser AppUser { get; set; }
public int AppUserId { get; set; }
}

View file

@ -2,56 +2,55 @@
using System;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
/// <summary>
/// Represents the progress a single user has on a given Chapter.
/// </summary>
//[Index(nameof(SeriesId), nameof(VolumeId), nameof(ChapterId), nameof(AppUserId), IsUnique = true)]
public class AppUserProgress : IEntityDate
{
/// <summary>
/// Represents the progress a single user has on a given Chapter.
/// Id of Entity
/// </summary>
//[Index(nameof(SeriesId), nameof(VolumeId), nameof(ChapterId), nameof(AppUserId), IsUnique = true)]
public class AppUserProgress : IEntityDate
{
/// <summary>
/// Id of Entity
/// </summary>
public int Id { get; set; }
/// <summary>
/// Pages Read for given Chapter
/// </summary>
public int PagesRead { get; set; }
/// <summary>
/// Volume belonging to Chapter
/// </summary>
public int VolumeId { get; set; }
/// <summary>
/// Series belonging to Chapter
/// </summary>
public int SeriesId { get; set; }
/// <summary>
/// Chapter
/// </summary>
public int ChapterId { get; set; }
/// <summary>
/// For Book Reader, represents the nearest passed anchor on the screen that can be used to resume scroll point
/// on next load
/// </summary>
public string BookScrollId { get; set; }
/// <summary>
/// When this was first created
/// </summary>
public DateTime Created { get; set; }
/// <summary>
/// Last date this was updated
/// </summary>
public DateTime LastModified { get; set; }
public int Id { get; set; }
/// <summary>
/// Pages Read for given Chapter
/// </summary>
public int PagesRead { get; set; }
/// <summary>
/// Volume belonging to Chapter
/// </summary>
public int VolumeId { get; set; }
/// <summary>
/// Series belonging to Chapter
/// </summary>
public int SeriesId { get; set; }
/// <summary>
/// Chapter
/// </summary>
public int ChapterId { get; set; }
/// <summary>
/// For Book Reader, represents the nearest passed anchor on the screen that can be used to resume scroll point
/// on next load
/// </summary>
public string BookScrollId { get; set; }
/// <summary>
/// When this was first created
/// </summary>
public DateTime Created { get; set; }
/// <summary>
/// Last date this was updated
/// </summary>
public DateTime LastModified { get; set; }
// Relationships
/// <summary>
/// Navigational Property for EF. Links to a unique AppUser
/// </summary>
public AppUser AppUser { get; set; }
/// <summary>
/// User this progress belongs to
/// </summary>
public int AppUserId { get; set; }
}
// Relationships
/// <summary>
/// Navigational Property for EF. Links to a unique AppUser
/// </summary>
public AppUser AppUser { get; set; }
/// <summary>
/// User this progress belongs to
/// </summary>
public int AppUserId { get; set; }
}

View file

@ -1,22 +1,21 @@

namespace API.Entities
namespace API.Entities;
public class AppUserRating
{
public class AppUserRating
{
public int Id { get; set; }
/// <summary>
/// A number between 0-5 that represents how good a series is.
/// </summary>
public int Rating { get; set; }
/// <summary>
/// A short summary the user can write when giving their review.
/// </summary>
public string Review { get; set; }
public int SeriesId { get; set; }
// Relationships
public int AppUserId { get; set; }
public AppUser AppUser { get; set; }
}
}
public int Id { get; set; }
/// <summary>
/// A number between 0-5 that represents how good a series is.
/// </summary>
public int Rating { get; set; }
/// <summary>
/// A short summary the user can write when giving their review.
/// </summary>
public string Review { get; set; }
public int SeriesId { get; set; }
// Relationships
public int AppUserId { get; set; }
public AppUser AppUser { get; set; }
}

View file

@ -1,10 +1,9 @@
using Microsoft.AspNetCore.Identity;
namespace API.Entities
namespace API.Entities;
public class AppUserRole : IdentityUserRole<int>
{
public class AppUserRole : IdentityUserRole<int>
{
public AppUser User { get; set; }
public AppRole Role { get; set; }
}
}
public AppUser User { get; set; }
public AppRole Role { get; set; }
}

View file

@ -5,116 +5,115 @@ using API.Entities.Interfaces;
using API.Parser;
using API.Services;
namespace API.Entities
namespace API.Entities;
public class Chapter : IEntityDate, IHasReadTimeEstimate
{
public class Chapter : IEntityDate, IHasReadTimeEstimate
public int Id { get; set; }
/// <summary>
/// Range of numbers. Chapter 2-4 -> "2-4". Chapter 2 -> "2".
/// </summary>
public string Range { get; set; }
/// <summary>
/// Smallest number of the Range. Can be a partial like Chapter 4.5
/// </summary>
public string Number { get; set; }
/// <summary>
/// The files that represent this Chapter
/// </summary>
public ICollection<MangaFile> Files { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Relative path to the (managed) image file representing the cover image
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
public bool CoverImageLocked { get; set; }
/// <summary>
/// Total number of pages in all MangaFiles
/// </summary>
public int Pages { get; set; }
/// <summary>
/// If this Chapter contains files that could only be identified as Series or has Special Identifier from filename
/// </summary>
public bool IsSpecial { get; set; }
/// <summary>
/// Used for books/specials to display custom title. For non-specials/books, will be set to <see cref="Range"/>
/// </summary>
public string Title { get; set; }
/// <summary>
/// Age Rating for the issue/chapter
/// </summary>
public AgeRating AgeRating { get; set; }
/// <summary>
/// Chapter title
/// </summary>
/// <remarks>This should not be confused with Title which is used for special filenames.</remarks>
public string TitleName { get; set; } = string.Empty;
/// <summary>
/// Date which chapter was released
/// </summary>
public DateTime ReleaseDate { get; set; }
/// <summary>
/// Summary for the Chapter/Issue
/// </summary>
public string Summary { get; set; }
/// <summary>
/// Language for the Chapter/Issue
/// </summary>
public string Language { get; set; }
/// <summary>
/// Total number of issues in the series
/// </summary>
public int TotalCount { get; set; } = 0;
/// <summary>
/// Number in the Total Count
/// </summary>
public int Count { get; set; } = 0;
/// <summary>
/// Total Word count of all chapters in this chapter.
/// </summary>
/// <remarks>Word Count is only available from EPUB files</remarks>
public long WordCount { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int MinHoursToRead { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int MaxHoursToRead { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int AvgHoursToRead { get; set; }
/// <summary>
/// All people attached at a Chapter level. Usually Comics will have different people per issue.
/// </summary>
public ICollection<Person> People { get; set; } = new List<Person>();
/// <summary>
/// Genres for the Chapter
/// </summary>
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
public ICollection<Tag> Tags { get; set; } = new List<Tag>();
// Relationships
public Volume Volume { get; set; }
public int VolumeId { get; set; }
public void UpdateFrom(ParserInfo info)
{
public int Id { get; set; }
/// <summary>
/// Range of numbers. Chapter 2-4 -> "2-4". Chapter 2 -> "2".
/// </summary>
public string Range { get; set; }
/// <summary>
/// Smallest number of the Range. Can be a partial like Chapter 4.5
/// </summary>
public string Number { get; set; }
/// <summary>
/// The files that represent this Chapter
/// </summary>
public ICollection<MangaFile> Files { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Relative path to the (managed) image file representing the cover image
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
public bool CoverImageLocked { get; set; }
/// <summary>
/// Total number of pages in all MangaFiles
/// </summary>
public int Pages { get; set; }
/// <summary>
/// If this Chapter contains files that could only be identified as Series or has Special Identifier from filename
/// </summary>
public bool IsSpecial { get; set; }
/// <summary>
/// Used for books/specials to display custom title. For non-specials/books, will be set to <see cref="Range"/>
/// </summary>
public string Title { get; set; }
/// <summary>
/// Age Rating for the issue/chapter
/// </summary>
public AgeRating AgeRating { get; set; }
/// <summary>
/// Chapter title
/// </summary>
/// <remarks>This should not be confused with Title which is used for special filenames.</remarks>
public string TitleName { get; set; } = string.Empty;
/// <summary>
/// Date which chapter was released
/// </summary>
public DateTime ReleaseDate { get; set; }
/// <summary>
/// Summary for the Chapter/Issue
/// </summary>
public string Summary { get; set; }
/// <summary>
/// Language for the Chapter/Issue
/// </summary>
public string Language { get; set; }
/// <summary>
/// Total number of issues in the series
/// </summary>
public int TotalCount { get; set; } = 0;
/// <summary>
/// Number in the Total Count
/// </summary>
public int Count { get; set; } = 0;
/// <summary>
/// Total Word count of all chapters in this chapter.
/// </summary>
/// <remarks>Word Count is only available from EPUB files</remarks>
public long WordCount { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int MinHoursToRead { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int MaxHoursToRead { get; set; }
/// <inheritdoc cref="IHasReadTimeEstimate"/>
public int AvgHoursToRead { get; set; }
/// <summary>
/// All people attached at a Chapter level. Usually Comics will have different people per issue.
/// </summary>
public ICollection<Person> People { get; set; } = new List<Person>();
/// <summary>
/// Genres for the Chapter
/// </summary>
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
public ICollection<Tag> Tags { get; set; } = new List<Tag>();
// Relationships
public Volume Volume { get; set; }
public int VolumeId { get; set; }
public void UpdateFrom(ParserInfo info)
Files ??= new List<MangaFile>();
IsSpecial = info.IsSpecialInfo();
if (IsSpecial)
{
Files ??= new List<MangaFile>();
IsSpecial = info.IsSpecialInfo();
if (IsSpecial)
{
Number = "0";
}
Title = (IsSpecial && info.Format == MangaFormat.Epub)
? info.Title
: Range;
Number = "0";
}
Title = (IsSpecial && info.Format == MangaFormat.Epub)
? info.Title
: Range;
}
}

View file

@ -2,56 +2,55 @@
using API.Entities.Metadata;
using Microsoft.EntityFrameworkCore;
namespace API.Entities
namespace API.Entities;
/// <summary>
/// Represents a user entered field that is used as a tagging and grouping mechanism
/// </summary>
[Index(nameof(Id), nameof(Promoted), IsUnique = true)]
public class CollectionTag
{
public int Id { get; set; }
/// <summary>
/// Represents a user entered field that is used as a tagging and grouping mechanism
/// Visible title of the Tag
/// </summary>
[Index(nameof(Id), nameof(Promoted), IsUnique = true)]
public class CollectionTag
public string Title { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
/// <summary>
/// Denotes if the CoverImage has been overridden by the user. If so, it will not be updated during normal scan operations.
/// </summary>
public bool CoverImageLocked { get; set; }
/// <summary>
/// A description of the tag
/// </summary>
public string Summary { get; set; }
/// <summary>
/// A normalized string used to check if the tag already exists in the DB
/// </summary>
public string NormalizedTitle { get; set; }
/// <summary>
/// A promoted collection tag will allow all linked seriesMetadata's Series to show for all users.
/// </summary>
public bool Promoted { get; set; }
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
/// <summary>
/// Not Used due to not using concurrency update
/// </summary>
public uint RowVersion { get; private set; }
/// <summary>
/// Not Used due to not using concurrency update
/// </summary>
public void OnSavingChanges()
{
public int Id { get; set; }
/// <summary>
/// Visible title of the Tag
/// </summary>
public string Title { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
/// <summary>
/// Denotes if the CoverImage has been overridden by the user. If so, it will not be updated during normal scan operations.
/// </summary>
public bool CoverImageLocked { get; set; }
/// <summary>
/// A description of the tag
/// </summary>
public string Summary { get; set; }
/// <summary>
/// A normalized string used to check if the tag already exists in the DB
/// </summary>
public string NormalizedTitle { get; set; }
/// <summary>
/// A promoted collection tag will allow all linked seriesMetadata's Series to show for all users.
/// </summary>
public bool Promoted { get; set; }
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
/// <summary>
/// Not Used due to not using concurrency update
/// </summary>
public uint RowVersion { get; private set; }
/// <summary>
/// Not Used due to not using concurrency update
/// </summary>
public void OnSavingChanges()
{
RowVersion++;
}
RowVersion++;
}
}

View file

@ -1,23 +1,22 @@
using System.ComponentModel;
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum LibraryType
{
public enum LibraryType
{
/// <summary>
/// Uses Manga regex for filename parsing
/// </summary>
[Description("Manga")]
Manga = 0,
/// <summary>
/// Uses Comic regex for filename parsing
/// </summary>
[Description("Comic")]
Comic = 1,
/// <summary>
/// Uses Manga regex for filename parsing also uses epub metadata
/// </summary>
[Description("Book")]
Book = 2,
}
/// <summary>
/// Uses Manga regex for filename parsing
/// </summary>
[Description("Manga")]
Manga = 0,
/// <summary>
/// Uses Comic regex for filename parsing
/// </summary>
[Description("Comic")]
Comic = 1,
/// <summary>
/// Uses Manga regex for filename parsing also uses epub metadata
/// </summary>
[Description("Book")]
Book = 2,
}

View file

@ -1,38 +1,37 @@
using System.ComponentModel;
namespace API.Entities.Enums
namespace API.Entities.Enums;
/// <summary>
/// Represents the format of the file
/// </summary>
public enum MangaFormat
{
/// <summary>
/// Represents the format of the file
/// Image file
/// See <see cref="Services.Tasks.Scanner.Parser.Parser.ImageFileExtensions"/> for supported extensions
/// </summary>
public enum MangaFormat
{
/// <summary>
/// Image file
/// See <see cref="Services.Tasks.Scanner.Parser.Parser.ImageFileExtensions"/> for supported extensions
/// </summary>
[Description("Image")]
Image = 0,
/// <summary>
/// Archive based file
/// See <see cref="Services.Tasks.Scanner.Parser.Parser.ArchiveFileExtensions"/> for supported extensions
/// </summary>
[Description("Archive")]
Archive = 1,
/// <summary>
/// Unknown. Not used.
/// </summary>
[Description("Unknown")]
Unknown = 2,
/// <summary>
/// EPUB File
/// </summary>
[Description("EPUB")]
Epub = 3,
/// <summary>
/// PDF File
/// </summary>
[Description("PDF")]
Pdf = 4
}
[Description("Image")]
Image = 0,
/// <summary>
/// Archive based file
/// See <see cref="Services.Tasks.Scanner.Parser.Parser.ArchiveFileExtensions"/> for supported extensions
/// </summary>
[Description("Archive")]
Archive = 1,
/// <summary>
/// Unknown. Not used.
/// </summary>
[Description("Unknown")]
Unknown = 2,
/// <summary>
/// EPUB File
/// </summary>
[Description("EPUB")]
Epub = 3,
/// <summary>
/// PDF File
/// </summary>
[Description("PDF")]
Pdf = 4
}

View file

@ -1,10 +1,9 @@
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum PageSplitOption
{
public enum PageSplitOption
{
SplitLeftToRight = 0,
SplitRightToLeft = 1,
NoSplit = 2,
FitSplit = 3
}
SplitLeftToRight = 0,
SplitRightToLeft = 1,
NoSplit = 2,
FitSplit = 3
}

View file

@ -1,31 +1,30 @@
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum PersonRole
{
public enum PersonRole
{
/// <summary>
/// Another role, not covered by other types
/// </summary>
Other = 1,
/// <summary>
/// Author or Writer
/// </summary>
Writer = 3,
Penciller = 4,
Inker = 5,
Colorist = 6,
Letterer = 7,
CoverArtist = 8,
Editor = 9,
Publisher = 10,
/// <summary>
/// Represents a character/person within the story
/// </summary>
Character = 11,
/// <summary>
/// The Translator
/// </summary>
Translator = 12
/// <summary>
/// Another role, not covered by other types
/// </summary>
Other = 1,
/// <summary>
/// Author or Writer
/// </summary>
Writer = 3,
Penciller = 4,
Inker = 5,
Colorist = 6,
Letterer = 7,
CoverArtist = 8,
Editor = 9,
Publisher = 10,
/// <summary>
/// Represents a character/person within the story
/// </summary>
Character = 11,
/// <summary>
/// The Translator
/// </summary>
Translator = 12
}
}

View file

@ -1,14 +1,13 @@
using System.ComponentModel;
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum ReaderMode
{
public enum ReaderMode
{
[Description("Left and Right")]
LeftRight = 0,
[Description("Up and Down")]
UpDown = 1,
[Description("Webtoon")]
Webtoon = 2
}
[Description("Left and Right")]
LeftRight = 0,
[Description("Up and Down")]
UpDown = 1,
[Description("Webtoon")]
Webtoon = 2
}

View file

@ -1,8 +1,7 @@
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum ReadingDirection
{
public enum ReadingDirection
{
LeftToRight = 0,
RightToLeft = 1
}
}
LeftToRight = 0,
RightToLeft = 1
}

View file

@ -1,10 +1,9 @@
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum ScalingOption
{
public enum ScalingOption
{
FitToHeight = 0,
FitToWidth = 1,
Original = 2,
Automatic = 3
}
}
FitToHeight = 0,
FitToWidth = 1,
Original = 2,
Automatic = 3
}

View file

@ -1,100 +1,99 @@
using System.ComponentModel;
namespace API.Entities.Enums
namespace API.Entities.Enums;
public enum ServerSettingKey
{
public enum ServerSettingKey
{
/// <summary>
/// Cron format for how often full library scans are performed.
/// </summary>
[Description("TaskScan")]
TaskScan = 0,
/// <summary>
/// Where files are cached. Not currently used.
/// </summary>
[Description("CacheDirectory")]
CacheDirectory = 1,
/// <summary>
/// Cron format for how often backups are taken.
/// </summary>
[Description("TaskBackup")]
TaskBackup = 2,
/// <summary>
/// Logging level for Server. Not managed in DB. Managed in appsettings.json and synced to DB.
/// </summary>
[Description("LoggingLevel")]
LoggingLevel = 3,
/// <summary>
/// Port server listens on. Not managed in DB. Managed in appsettings.json and synced to DB.
/// </summary>
[Description("Port")]
Port = 4,
/// <summary>
/// Where the backups are stored.
/// </summary>
[Description("BackupDirectory")]
BackupDirectory = 5,
/// <summary>
/// Allow anonymous data to be reported to KavitaStats
/// </summary>
[Description("AllowStatCollection")]
AllowStatCollection = 6,
/// <summary>
/// Is OPDS enabled for the server
/// </summary>
[Description("EnableOpds")]
EnableOpds = 7,
/// <summary>
/// Is Authentication needed for non-admin accounts
/// </summary>
/// <remarks>Deprecated. This is no longer used v0.5.1+. Assume Authentication is always in effect</remarks>
[Description("EnableAuthentication")]
EnableAuthentication = 8,
/// <summary>
/// Base Url for the server. Not Implemented.
/// </summary>
[Description("BaseUrl")]
BaseUrl = 9,
/// <summary>
/// Represents this installation of Kavita. Is tied to Stat reporting but has no information about user or files.
/// </summary>
[Description("InstallId")]
InstallId = 10,
/// <summary>
/// Represents the version the software is running.
/// </summary>
/// <remarks>This will be updated on Startup to the latest release. Provides ability to detect if certain migrations need to be run.</remarks>
[Description("InstallVersion")]
InstallVersion = 11,
/// <summary>
/// Location of where bookmarks are stored
/// </summary>
[Description("BookmarkDirectory")]
BookmarkDirectory = 12,
/// <summary>
/// If SMTP is enabled on the server
/// </summary>
[Description("CustomEmailService")]
EmailServiceUrl = 13,
/// <summary>
/// If Kavita should save bookmarks as WebP images
/// </summary>
[Description("ConvertBookmarkToWebP")]
ConvertBookmarkToWebP = 14,
/// <summary>
/// If the Swagger UI Should be exposed. Does not require authentication, but does require a JWT.
/// </summary>
[Description("EnableSwaggerUi")]
EnableSwaggerUi = 15,
/// <summary>
/// Total Number of Backups to maintain before cleaning. Default 30, min 1.
/// </summary>
[Description("TotalBackups")]
TotalBackups = 16,
/// <summary>
/// If Kavita should watch the library folders and process changes
/// </summary>
[Description("EnableFolderWatching")]
EnableFolderWatching = 17,
}
/// <summary>
/// Cron format for how often full library scans are performed.
/// </summary>
[Description("TaskScan")]
TaskScan = 0,
/// <summary>
/// Where files are cached. Not currently used.
/// </summary>
[Description("CacheDirectory")]
CacheDirectory = 1,
/// <summary>
/// Cron format for how often backups are taken.
/// </summary>
[Description("TaskBackup")]
TaskBackup = 2,
/// <summary>
/// Logging level for Server. Not managed in DB. Managed in appsettings.json and synced to DB.
/// </summary>
[Description("LoggingLevel")]
LoggingLevel = 3,
/// <summary>
/// Port server listens on. Not managed in DB. Managed in appsettings.json and synced to DB.
/// </summary>
[Description("Port")]
Port = 4,
/// <summary>
/// Where the backups are stored.
/// </summary>
[Description("BackupDirectory")]
BackupDirectory = 5,
/// <summary>
/// Allow anonymous data to be reported to KavitaStats
/// </summary>
[Description("AllowStatCollection")]
AllowStatCollection = 6,
/// <summary>
/// Is OPDS enabled for the server
/// </summary>
[Description("EnableOpds")]
EnableOpds = 7,
/// <summary>
/// Is Authentication needed for non-admin accounts
/// </summary>
/// <remarks>Deprecated. This is no longer used v0.5.1+. Assume Authentication is always in effect</remarks>
[Description("EnableAuthentication")]
EnableAuthentication = 8,
/// <summary>
/// Base Url for the server. Not Implemented.
/// </summary>
[Description("BaseUrl")]
BaseUrl = 9,
/// <summary>
/// Represents this installation of Kavita. Is tied to Stat reporting but has no information about user or files.
/// </summary>
[Description("InstallId")]
InstallId = 10,
/// <summary>
/// Represents the version the software is running.
/// </summary>
/// <remarks>This will be updated on Startup to the latest release. Provides ability to detect if certain migrations need to be run.</remarks>
[Description("InstallVersion")]
InstallVersion = 11,
/// <summary>
/// Location of where bookmarks are stored
/// </summary>
[Description("BookmarkDirectory")]
BookmarkDirectory = 12,
/// <summary>
/// If SMTP is enabled on the server
/// </summary>
[Description("CustomEmailService")]
EmailServiceUrl = 13,
/// <summary>
/// If Kavita should save bookmarks as WebP images
/// </summary>
[Description("ConvertBookmarkToWebP")]
ConvertBookmarkToWebP = 14,
/// <summary>
/// If the Swagger UI Should be exposed. Does not require authentication, but does require a JWT.
/// </summary>
[Description("EnableSwaggerUi")]
EnableSwaggerUi = 15,
/// <summary>
/// Total Number of Backups to maintain before cleaning. Default 30, min 1.
/// </summary>
[Description("TotalBackups")]
TotalBackups = 16,
/// <summary>
/// If Kavita should watch the library folders and process changes
/// </summary>
[Description("EnableFolderWatching")]
EnableFolderWatching = 17,
}

View file

@ -1,20 +1,19 @@

using System;
namespace API.Entities
{
public class FolderPath
{
public int Id { get; set; }
public string Path { get; set; }
/// <summary>
/// Used when scanning to see if we can skip if nothing has changed
/// </summary>
/// <remarks>Time stored in UTC</remarks>
public DateTime LastScanned { get; set; }
namespace API.Entities;
// Relationship
public Library Library { get; set; }
public int LibraryId { get; set; }
}
public class FolderPath
{
public int Id { get; set; }
public string Path { get; set; }
/// <summary>
/// Used when scanning to see if we can skip if nothing has changed
/// </summary>
/// <remarks>Time stored in UTC</remarks>
public DateTime LastScanned { get; set; }
// Relationship
public Library Library { get; set; }
public int LibraryId { get; set; }
}

View file

@ -2,17 +2,16 @@
using API.Entities.Metadata;
using Microsoft.EntityFrameworkCore;
namespace API.Entities
{
[Index(nameof(NormalizedTitle), nameof(ExternalTag), IsUnique = true)]
public class Genre
{
public int Id { get; set; }
public string Title { get; set; }
public string NormalizedTitle { get; set; }
public bool ExternalTag { get; set; }
namespace API.Entities;
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
public ICollection<Chapter> Chapters { get; set; }
}
[Index(nameof(NormalizedTitle), nameof(ExternalTag), IsUnique = true)]
public class Genre
{
public int Id { get; set; }
public string Title { get; set; }
public string NormalizedTitle { get; set; }
public bool ExternalTag { get; set; }
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
public ICollection<Chapter> Chapters { get; set; }
}

View file

@ -1,10 +1,9 @@
using System;
namespace API.Entities.Interfaces
namespace API.Entities.Interfaces;
public interface IEntityDate
{
public interface IEntityDate
{
DateTime Created { get; set; }
DateTime LastModified { get; set; }
}
}
DateTime Created { get; set; }
DateTime LastModified { get; set; }
}

View file

@ -1,19 +1,18 @@
namespace API.Entities.Interfaces
namespace API.Entities.Interfaces;
/// <summary>
/// An interface abstracting an entity that has a concurrency token.
/// </summary>
public interface IHasConcurrencyToken
{
/// <summary>
/// An interface abstracting an entity that has a concurrency token.
/// Gets the version of this row. Acts as a concurrency token.
/// </summary>
public interface IHasConcurrencyToken
{
/// <summary>
/// Gets the version of this row. Acts as a concurrency token.
/// </summary>
uint RowVersion { get; }
uint RowVersion { get; }
/// <summary>
/// Called when saving changes to this entity.
/// </summary>
void OnSavingChanges();
/// <summary>
/// Called when saving changes to this entity.
/// </summary>
void OnSavingChanges();
}
}
}

View file

@ -5,39 +5,38 @@ using System.Linq;
using API.Entities.Enums;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
public class Library : IEntityDate
{
public class Library : IEntityDate
public int Id { get; set; }
public string Name { get; set; }
/// <summary>
/// Update this summary with a way it's used, else let's remove it.
/// </summary>
[Obsolete("This has never been coded for. Likely we can remove it.")]
public string CoverImage { get; set; }
public LibraryType Type { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Last time Library was scanned
/// </summary>
/// <remarks>Time stored in UTC</remarks>
public DateTime LastScanned { get; set; }
public ICollection<FolderPath> Folders { get; set; }
public ICollection<AppUser> AppUsers { get; set; }
public ICollection<Series> Series { get; set; }
// Methods
/// <summary>
/// Has there been any modifications to the FolderPath's directory since the <see cref="FolderPath.LastScanned"/> date
/// </summary>
/// <returns></returns>
public bool AnyModificationsSinceLastScan()
{
public int Id { get; set; }
public string Name { get; set; }
/// <summary>
/// Update this summary with a way it's used, else let's remove it.
/// </summary>
[Obsolete("This has never been coded for. Likely we can remove it.")]
public string CoverImage { get; set; }
public LibraryType Type { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Last time Library was scanned
/// </summary>
/// <remarks>Time stored in UTC</remarks>
public DateTime LastScanned { get; set; }
public ICollection<FolderPath> Folders { get; set; }
public ICollection<AppUser> AppUsers { get; set; }
public ICollection<Series> Series { get; set; }
// Methods
/// <summary>
/// Has there been any modifications to the FolderPath's directory since the <see cref="FolderPath.LastScanned"/> date
/// </summary>
/// <returns></returns>
public bool AnyModificationsSinceLastScan()
{
// NOTE: I don't think we can do this due to NTFS
return Folders.All(folder => File.GetLastWriteTimeUtc(folder.Path) > folder.LastScanned);
}
// NOTE: I don't think we can do this due to NTFS
return Folders.All(folder => File.GetLastWriteTimeUtc(folder.Path) > folder.LastScanned);
}
}

View file

@ -4,48 +4,47 @@ using System.IO;
using API.Entities.Enums;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
/// <summary>
/// Represents a wrapper to the underlying file. This provides information around file, like number of pages, format, etc.
/// </summary>
public class MangaFile : IEntityDate
{
public int Id { get; set; }
/// <summary>
/// Represents a wrapper to the underlying file. This provides information around file, like number of pages, format, etc.
/// Absolute path to the archive file
/// </summary>
public class MangaFile : IEntityDate
public string FilePath { get; set; }
/// <summary>
/// Number of pages for the given file
/// </summary>
public int Pages { get; set; }
public MangaFormat Format { get; set; }
/// <inheritdoc cref="IEntityDate.Created"/>
public DateTime Created { get; set; }
/// <summary>
/// Last time underlying file was modified
/// </summary>
/// <remarks>This gets updated anytime the file is scanned</remarks>
public DateTime LastModified { get; set; }
/// <summary>
/// Last time file analysis ran on this file
/// </summary>
public DateTime LastFileAnalysis { get; set; }
// Relationship Mapping
public Chapter Chapter { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Updates the Last Modified time of the underlying file to the LastWriteTime
/// </summary>
public void UpdateLastModified()
{
public int Id { get; set; }
/// <summary>
/// Absolute path to the archive file
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// Number of pages for the given file
/// </summary>
public int Pages { get; set; }
public MangaFormat Format { get; set; }
/// <inheritdoc cref="IEntityDate.Created"/>
public DateTime Created { get; set; }
/// <summary>
/// Last time underlying file was modified
/// </summary>
/// <remarks>This gets updated anytime the file is scanned</remarks>
public DateTime LastModified { get; set; }
/// <summary>
/// Last time file analysis ran on this file
/// </summary>
public DateTime LastFileAnalysis { get; set; }
// Relationship Mapping
public Chapter Chapter { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Updates the Last Modified time of the underlying file to the LastWriteTime
/// </summary>
public void UpdateLastModified()
{
LastModified = File.GetLastWriteTime(FilePath);
}
LastModified = File.GetLastWriteTime(FilePath);
}
}

View file

@ -4,84 +4,83 @@ using API.Entities.Enums;
using API.Entities.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace API.Entities.Metadata
namespace API.Entities.Metadata;
[Index(nameof(Id), nameof(SeriesId), IsUnique = true)]
public class SeriesMetadata : IHasConcurrencyToken
{
[Index(nameof(Id), nameof(SeriesId), IsUnique = true)]
public class SeriesMetadata : IHasConcurrencyToken
public int Id { get; set; }
public string Summary { get; set; } = string.Empty;
public ICollection<CollectionTag> CollectionTags { get; set; }
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
public ICollection<Tag> Tags { get; set; } = new List<Tag>();
/// <summary>
/// All people attached at a Series level.
/// </summary>
public ICollection<Person> People { get; set; } = new List<Person>();
/// <summary>
/// Highest Age Rating from all Chapters
/// </summary>
public AgeRating AgeRating { get; set; }
/// <summary>
/// Earliest Year from all chapters
/// </summary>
public int ReleaseYear { get; set; }
/// <summary>
/// Language of the content (BCP-47 code)
/// </summary>
public string Language { get; set; } = string.Empty;
/// <summary>
/// Total number of issues/volumes in the series
/// </summary>
public int TotalCount { get; set; } = 0;
/// <summary>
/// Max number of issues/volumes in the series (Max of Volume/Issue field in ComicInfo)
/// </summary>
public int MaxCount { get; set; } = 0;
public PublicationStatus PublicationStatus { get; set; }
// Locks
public bool LanguageLocked { get; set; }
public bool SummaryLocked { get; set; }
/// <summary>
/// Locked by user so metadata updates from scan loop will not override AgeRating
/// </summary>
public bool AgeRatingLocked { get; set; }
/// <summary>
/// Locked by user so metadata updates from scan loop will not override PublicationStatus
/// </summary>
public bool PublicationStatusLocked { get; set; }
public bool GenresLocked { get; set; }
public bool TagsLocked { get; set; }
public bool WriterLocked { get; set; }
public bool CharacterLocked { get; set; }
public bool ColoristLocked { get; set; }
public bool EditorLocked { get; set; }
public bool InkerLocked { get; set; }
public bool LettererLocked { get; set; }
public bool PencillerLocked { get; set; }
public bool PublisherLocked { get; set; }
public bool TranslatorLocked { get; set; }
public bool CoverArtistLocked { get; set; }
// Relationship
public Series Series { get; set; }
public int SeriesId { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
public int Id { get; set; }
public string Summary { get; set; } = string.Empty;
public ICollection<CollectionTag> CollectionTags { get; set; }
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
public ICollection<Tag> Tags { get; set; } = new List<Tag>();
/// <summary>
/// All people attached at a Series level.
/// </summary>
public ICollection<Person> People { get; set; } = new List<Person>();
/// <summary>
/// Highest Age Rating from all Chapters
/// </summary>
public AgeRating AgeRating { get; set; }
/// <summary>
/// Earliest Year from all chapters
/// </summary>
public int ReleaseYear { get; set; }
/// <summary>
/// Language of the content (BCP-47 code)
/// </summary>
public string Language { get; set; } = string.Empty;
/// <summary>
/// Total number of issues/volumes in the series
/// </summary>
public int TotalCount { get; set; } = 0;
/// <summary>
/// Max number of issues/volumes in the series (Max of Volume/Issue field in ComicInfo)
/// </summary>
public int MaxCount { get; set; } = 0;
public PublicationStatus PublicationStatus { get; set; }
// Locks
public bool LanguageLocked { get; set; }
public bool SummaryLocked { get; set; }
/// <summary>
/// Locked by user so metadata updates from scan loop will not override AgeRating
/// </summary>
public bool AgeRatingLocked { get; set; }
/// <summary>
/// Locked by user so metadata updates from scan loop will not override PublicationStatus
/// </summary>
public bool PublicationStatusLocked { get; set; }
public bool GenresLocked { get; set; }
public bool TagsLocked { get; set; }
public bool WriterLocked { get; set; }
public bool CharacterLocked { get; set; }
public bool ColoristLocked { get; set; }
public bool EditorLocked { get; set; }
public bool InkerLocked { get; set; }
public bool LettererLocked { get; set; }
public bool PencillerLocked { get; set; }
public bool PublisherLocked { get; set; }
public bool TranslatorLocked { get; set; }
public bool CoverArtistLocked { get; set; }
// Relationship
public Series Series { get; set; }
public int SeriesId { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
RowVersion++;
}
}

View file

@ -2,23 +2,22 @@
using API.Entities.Enums;
using API.Entities.Metadata;
namespace API.Entities
{
public enum ProviderSource
{
Local = 1,
External = 2
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string NormalizedName { get; set; }
public PersonRole Role { get; set; }
//public ProviderSource Source { get; set; }
namespace API.Entities;
// Relationships
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
public ICollection<Chapter> ChapterMetadatas { get; set; }
}
public enum ProviderSource
{
Local = 1,
External = 2
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string NormalizedName { get; set; }
public PersonRole Role { get; set; }
//public ProviderSource Source { get; set; }
// Relationships
public ICollection<SeriesMetadata> SeriesMetadatas { get; set; }
public ICollection<Chapter> ChapterMetadatas { get; set; }
}

View file

@ -2,38 +2,37 @@
using System.Collections.Generic;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
/// <summary>
/// This is a collection of <see cref="ReadingListItem"/> which represent individual chapters and an order.
/// </summary>
public class ReadingList : IEntityDate
{
public int Id { get; init; }
public string Title { get; set; }
/// <summary>
/// This is a collection of <see cref="ReadingListItem"/> which represent individual chapters and an order.
/// A normalized string used to check if the reading list already exists in the DB
/// </summary>
public class ReadingList : IEntityDate
{
public int Id { get; init; }
public string Title { get; set; }
/// <summary>
/// A normalized string used to check if the reading list already exists in the DB
/// </summary>
public string NormalizedTitle { get; set; }
public string Summary { get; set; }
/// <summary>
/// Reading lists that are promoted are only done by admins
/// </summary>
public bool Promoted { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
public bool CoverImageLocked { get; set; }
public string NormalizedTitle { get; set; }
public string Summary { get; set; }
/// <summary>
/// Reading lists that are promoted are only done by admins
/// </summary>
public bool Promoted { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
public bool CoverImageLocked { get; set; }
public ICollection<ReadingListItem> Items { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
public ICollection<ReadingListItem> Items { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
// Relationships
public int AppUserId { get; set; }
public AppUser AppUser { get; set; }
// Relationships
public int AppUserId { get; set; }
public AppUser AppUser { get; set; }
}
}

View file

@ -1,24 +1,23 @@
namespace API.Entities
namespace API.Entities;
public class ReadingListItem
{
public class ReadingListItem
{
public int Id { get; init; }
public int SeriesId { get; set; }
public int VolumeId { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Order of the chapter within a Reading List
/// </summary>
public int Order { get; set; }
public int Id { get; init; }
public int SeriesId { get; set; }
public int VolumeId { get; set; }
public int ChapterId { get; set; }
/// <summary>
/// Order of the chapter within a Reading List
/// </summary>
public int Order { get; set; }
// Relationship
public ReadingList ReadingList { get; set; }
public int ReadingListId { get; set; }
// Relationship
public ReadingList ReadingList { get; set; }
public int ReadingListId { get; set; }
// Keep these for easy join statements
public Series Series { get; set; }
public Volume Volume { get; set; }
public Chapter Chapter { get; set; }
// Keep these for easy join statements
public Series Series { get; set; }
public Volume Volume { get; set; }
public Chapter Chapter { get; set; }
}
}

View file

@ -2,25 +2,24 @@
using API.Entities.Enums;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
public class ServerSetting : IHasConcurrencyToken
{
public class ServerSetting : IHasConcurrencyToken
[Key]
public ServerSettingKey Key { get; set; }
/// <summary>
/// The value of the Setting. Converter knows how to convert to the correct type
/// </summary>
public string Value { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
[Key]
public ServerSettingKey Key { get; set; }
/// <summary>
/// The value of the Setting. Converter knows how to convert to the correct type
/// </summary>
public string Value { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; private set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
RowVersion++;
}
}

View file

@ -2,45 +2,44 @@
using System.Collections.Generic;
using API.Entities.Interfaces;
namespace API.Entities
namespace API.Entities;
public class Volume : IEntityDate, IHasReadTimeEstimate
{
public class Volume : IEntityDate, IHasReadTimeEstimate
{
public int Id { get; set; }
/// <summary>
/// A String representation of the volume number. Allows for floats.
/// </summary>
/// <remarks>For Books with Series_index, this will map to the Series Index.</remarks>
public string Name { get; set; }
/// <summary>
/// The minimum number in the Name field in Int form
/// </summary>
public int Number { get; set; }
public IList<Chapter> Chapters { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
/// <summary>
/// Total pages of all chapters in this volume
/// </summary>
public int Pages { get; set; }
/// <summary>
/// Total Word count of all chapters in this volume.
/// </summary>
/// <remarks>Word Count is only available from EPUB files</remarks>
public long WordCount { get; set; }
public int MinHoursToRead { get; set; }
public int MaxHoursToRead { get; set; }
public int AvgHoursToRead { get; set; }
public int Id { get; set; }
/// <summary>
/// A String representation of the volume number. Allows for floats.
/// </summary>
/// <remarks>For Books with Series_index, this will map to the Series Index.</remarks>
public string Name { get; set; }
/// <summary>
/// The minimum number in the Name field in Int form
/// </summary>
public int Number { get; set; }
public IList<Chapter> Chapters { get; set; }
public DateTime Created { get; set; }
public DateTime LastModified { get; set; }
/// <summary>
/// Absolute path to the (managed) image file
/// </summary>
/// <remarks>The file is managed internally to Kavita's APPDIR</remarks>
public string CoverImage { get; set; }
/// <summary>
/// Total pages of all chapters in this volume
/// </summary>
public int Pages { get; set; }
/// <summary>
/// Total Word count of all chapters in this volume.
/// </summary>
/// <remarks>Word Count is only available from EPUB files</remarks>
public long WordCount { get; set; }
public int MinHoursToRead { get; set; }
public int MaxHoursToRead { get; set; }
public int AvgHoursToRead { get; set; }
// Relationships
public Series Series { get; set; }
public int SeriesId { get; set; }
// Relationships
public Series Series { get; set; }
public int SeriesId { get; set; }
}
}