mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-06-03 05:34:21 -04:00
Implemented Publication Status in SeriesMetadata and the ability to filter it. (#915)
This commit is contained in:
parent
f8e0fb8a27
commit
2fbcf203aa
@ -93,6 +93,27 @@ public class MetadataController : BaseApiController
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fetches all publication status' from the instance
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryIds">String separated libraryIds or null for all publication status</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("publication-status")]
|
||||||
|
public async Task<ActionResult<IList<AgeRatingDto>>> GetAllPublicationStatus(string? libraryIds)
|
||||||
|
{
|
||||||
|
var ids = libraryIds?.Split(",").Select(int.Parse).ToList();
|
||||||
|
if (ids != null && ids.Count > 0)
|
||||||
|
{
|
||||||
|
return Ok(await _unitOfWork.SeriesRepository.GetAllPublicationStatusesDtosForLibrariesAsync(ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(Enum.GetValues<PublicationStatus>().Select(t => new PublicationStatusDto()
|
||||||
|
{
|
||||||
|
Title = t.ToDescription(),
|
||||||
|
Value = t
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches all age ratings from the instance
|
/// Fetches all age ratings from the instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -53,7 +53,8 @@ public class OpdsController : BaseApiController
|
|||||||
CollectionTags = new List<int>(),
|
CollectionTags = new List<int>(),
|
||||||
CoverArtist = new List<int>(),
|
CoverArtist = new List<int>(),
|
||||||
ReadStatus = new ReadStatus(),
|
ReadStatus = new ReadStatus(),
|
||||||
SortOptions = null
|
SortOptions = null,
|
||||||
|
PublicationStatus = new List<PublicationStatus>()
|
||||||
};
|
};
|
||||||
private readonly ChapterSortComparer _chapterSortComparer = new ChapterSortComparer();
|
private readonly ChapterSortComparer _chapterSortComparer = new ChapterSortComparer();
|
||||||
|
|
||||||
|
@ -70,6 +70,14 @@ namespace API.DTOs
|
|||||||
/// Language for the Chapter/Issue
|
/// Language for the Chapter/Issue
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; }
|
public string Language { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Number in the TotalCount of issues
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of issues for the series
|
||||||
|
/// </summary>
|
||||||
|
public int TotalCount { get; set; }
|
||||||
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
|
public ICollection<PersonDto> Writers { get; set; } = new List<PersonDto>();
|
||||||
public ICollection<PersonDto> Penciller { get; set; } = new List<PersonDto>();
|
public ICollection<PersonDto> Penciller { get; set; } = new List<PersonDto>();
|
||||||
public ICollection<PersonDto> Inker { get; set; } = new List<PersonDto>();
|
public ICollection<PersonDto> Inker { get; set; } = new List<PersonDto>();
|
||||||
|
@ -89,6 +89,10 @@ namespace API.DTOs.Filtering
|
|||||||
/// Languages (ISO 639-1 code) to filter by. Empty list will return everything back
|
/// Languages (ISO 639-1 code) to filter by. Empty list will return everything back
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<string> Languages { get; init; } = new List<string>();
|
public IList<string> Languages { get; init; } = new List<string>();
|
||||||
|
/// <summary>
|
||||||
|
/// Publication statuses to filter by. Empty list will return everything back
|
||||||
|
/// </summary>
|
||||||
|
public IList<PublicationStatus> PublicationStatus { get; init; } = new List<PublicationStatus>();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
API/DTOs/Metadata/PublicationStatusDto.cs
Normal file
9
API/DTOs/Metadata/PublicationStatusDto.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using API.Entities.Enums;
|
||||||
|
|
||||||
|
namespace API.DTOs.Metadata;
|
||||||
|
|
||||||
|
public class PublicationStatusDto
|
||||||
|
{
|
||||||
|
public PublicationStatus Value { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
}
|
@ -43,6 +43,18 @@ namespace API.DTOs
|
|||||||
/// Language of the content (ISO 639-1 code)
|
/// Language of the content (ISO 639-1 code)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; } = string.Empty;
|
public string Language { get; set; } = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// Number in the TotalCount of issues
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of issues for the series
|
||||||
|
/// </summary>
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Publication status of the Series
|
||||||
|
/// </summary>
|
||||||
|
public PublicationStatus PublicationStatus { get; set; }
|
||||||
|
|
||||||
public int SeriesId { get; set; }
|
public int SeriesId { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,17 @@ namespace API.Data.Metadata
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A representation of a ComicInfo.xml file
|
/// A representation of a ComicInfo.xml file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>See reference of the loose spec here: https://github.com/Kussie/ComicInfoStandard/blob/main/ComicInfo.xsd</remarks>
|
/// <remarks>See reference of the loose spec here: https://anansi-project.github.io/docs/comicinfo/documentation</remarks>
|
||||||
public class ComicInfo
|
public class ComicInfo
|
||||||
{
|
{
|
||||||
public string Summary { get; set; } = string.Empty;
|
public string Summary { get; set; } = string.Empty;
|
||||||
public string Title { get; set; } = string.Empty;
|
public string Title { get; set; } = string.Empty;
|
||||||
public string Series { get; set; } = string.Empty;
|
public string Series { get; set; } = string.Empty;
|
||||||
public string Number { get; set; } = string.Empty;
|
public string Number { get; set; } = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// The total number of items in the series.
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; } = 0;
|
||||||
public string Volume { get; set; } = string.Empty;
|
public string Volume { get; set; } = string.Empty;
|
||||||
public string Notes { get; set; } = string.Empty;
|
public string Notes { get; set; } = string.Empty;
|
||||||
public string Genre { get; set; } = string.Empty;
|
public string Genre { get; set; } = string.Empty;
|
||||||
|
1345
API/Data/Migrations/20220108200822_CountMetadata.Designer.cs
generated
Normal file
1345
API/Data/Migrations/20220108200822_CountMetadata.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
API/Data/Migrations/20220108200822_CountMetadata.cs
Normal file
37
API/Data/Migrations/20220108200822_CountMetadata.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace API.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class CountMetadata : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Count",
|
||||||
|
table: "SeriesMetadata",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "Count",
|
||||||
|
table: "Chapter",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Count",
|
||||||
|
table: "SeriesMetadata");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Count",
|
||||||
|
table: "Chapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1351
API/Data/Migrations/20220108202027_PublicationStatus.Designer.cs
generated
Normal file
1351
API/Data/Migrations/20220108202027_PublicationStatus.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
API/Data/Migrations/20220108202027_PublicationStatus.cs
Normal file
37
API/Data/Migrations/20220108202027_PublicationStatus.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace API.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class PublicationStatus : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "PublicationStatus",
|
||||||
|
table: "SeriesMetadata",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "TotalCount",
|
||||||
|
table: "Chapter",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "PublicationStatus",
|
||||||
|
table: "SeriesMetadata");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "TotalCount",
|
||||||
|
table: "Chapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -299,6 +299,9 @@ namespace API.Data.Migrations
|
|||||||
b.Property<int>("AgeRating")
|
b.Property<int>("AgeRating")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("CoverImage")
|
b.Property<string>("CoverImage")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
@ -338,6 +341,9 @@ namespace API.Data.Migrations
|
|||||||
b.Property<string>("TitleName")
|
b.Property<string>("TitleName")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("TotalCount")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("VolumeId")
|
b.Property<int>("VolumeId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -494,9 +500,15 @@ namespace API.Data.Migrations
|
|||||||
b.Property<int>("AgeRating")
|
b.Property<int>("AgeRating")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("Count")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("Language")
|
b.Property<string>("Language")
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<int>("PublicationStatus")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("ReleaseYear")
|
b.Property<int>("ReleaseYear")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ public interface ISeriesRepository
|
|||||||
Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds);
|
Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds);
|
||||||
Task<IList<AgeRatingDto>> GetAllAgeRatingsDtosForLibrariesAsync(List<int> libraryIds);
|
Task<IList<AgeRatingDto>> GetAllAgeRatingsDtosForLibrariesAsync(List<int> libraryIds);
|
||||||
Task<IList<LanguageDto>> GetAllLanguagesForLibrariesAsync(List<int> libraryIds);
|
Task<IList<LanguageDto>> GetAllLanguagesForLibrariesAsync(List<int> libraryIds);
|
||||||
|
Task<IList<PublicationStatusDto>> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SeriesRepository : ISeriesRepository
|
public class SeriesRepository : ISeriesRepository
|
||||||
@ -425,7 +426,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
private IList<MangaFormat> ExtractFilters(int libraryId, int userId, FilterDto filter, ref List<int> userLibraries,
|
private IList<MangaFormat> ExtractFilters(int libraryId, int userId, FilterDto filter, ref List<int> userLibraries,
|
||||||
out List<int> allPeopleIds, out bool hasPeopleFilter, out bool hasGenresFilter, out bool hasCollectionTagFilter,
|
out List<int> allPeopleIds, out bool hasPeopleFilter, out bool hasGenresFilter, out bool hasCollectionTagFilter,
|
||||||
out bool hasRatingFilter, out bool hasProgressFilter, out IList<int> seriesIds, out bool hasAgeRating, out bool hasTagsFilter,
|
out bool hasRatingFilter, out bool hasProgressFilter, out IList<int> seriesIds, out bool hasAgeRating, out bool hasTagsFilter,
|
||||||
out bool hasLanguageFilter)
|
out bool hasLanguageFilter, out bool hasPublicationFilter)
|
||||||
{
|
{
|
||||||
var formats = filter.GetSqlFilter();
|
var formats = filter.GetSqlFilter();
|
||||||
|
|
||||||
@ -454,6 +455,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
hasAgeRating = filter.AgeRating.Count > 0;
|
hasAgeRating = filter.AgeRating.Count > 0;
|
||||||
hasTagsFilter = filter.Tags.Count > 0;
|
hasTagsFilter = filter.Tags.Count > 0;
|
||||||
hasLanguageFilter = filter.Languages.Count > 0;
|
hasLanguageFilter = filter.Languages.Count > 0;
|
||||||
|
hasPublicationFilter = filter.PublicationStatus.Count > 0;
|
||||||
|
|
||||||
|
|
||||||
bool ProgressComparison(int pagesRead, int totalPages)
|
bool ProgressComparison(int pagesRead, int totalPages)
|
||||||
@ -541,7 +543,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
var formats = ExtractFilters(libraryId, userId, filter, ref userLibraries,
|
var formats = ExtractFilters(libraryId, userId, filter, ref userLibraries,
|
||||||
out var allPeopleIds, out var hasPeopleFilter, out var hasGenresFilter,
|
out var allPeopleIds, out var hasPeopleFilter, out var hasGenresFilter,
|
||||||
out var hasCollectionTagFilter, out var hasRatingFilter, out var hasProgressFilter,
|
out var hasCollectionTagFilter, out var hasRatingFilter, out var hasProgressFilter,
|
||||||
out var seriesIds, out var hasAgeRating, out var hasTagsFilter, out var hasLanguageFilter);
|
out var seriesIds, out var hasAgeRating, out var hasTagsFilter, out var hasLanguageFilter, out var hasPublicationFilter);
|
||||||
|
|
||||||
var query = _context.Series
|
var query = _context.Series
|
||||||
.Where(s => userLibraries.Contains(s.LibraryId)
|
.Where(s => userLibraries.Contains(s.LibraryId)
|
||||||
@ -555,6 +557,7 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
&& (!hasAgeRating || filter.AgeRating.Contains(s.Metadata.AgeRating))
|
&& (!hasAgeRating || filter.AgeRating.Contains(s.Metadata.AgeRating))
|
||||||
&& (!hasTagsFilter || s.Metadata.Tags.Any(t => filter.Tags.Contains(t.Id)))
|
&& (!hasTagsFilter || s.Metadata.Tags.Any(t => filter.Tags.Contains(t.Id)))
|
||||||
&& (!hasLanguageFilter || filter.Languages.Contains(s.Metadata.Language))
|
&& (!hasLanguageFilter || filter.Languages.Contains(s.Metadata.Language))
|
||||||
|
&& (!hasPublicationFilter || filter.PublicationStatus.Contains(s.Metadata.PublicationStatus))
|
||||||
)
|
)
|
||||||
.AsNoTracking();
|
.AsNoTracking();
|
||||||
|
|
||||||
@ -769,4 +772,18 @@ public class SeriesRepository : ISeriesRepository
|
|||||||
IsoCode = s
|
IsoCode = s
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IList<PublicationStatusDto>> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds)
|
||||||
|
{
|
||||||
|
return await _context.Series
|
||||||
|
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||||
|
.Select(s => s.Metadata.PublicationStatus)
|
||||||
|
.Distinct()
|
||||||
|
.Select(s => new PublicationStatusDto()
|
||||||
|
{
|
||||||
|
Value = s,
|
||||||
|
Title = s.ToDescription()
|
||||||
|
})
|
||||||
|
.ToListAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,14 @@ namespace API.Entities
|
|||||||
/// Language for the Chapter/Issue
|
/// Language for the Chapter/Issue
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; }
|
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>
|
/// <summary>
|
||||||
|
23
API/Entities/Enums/PublicationStatus.cs
Normal file
23
API/Entities/Enums/PublicationStatus.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace API.Entities.Enums;
|
||||||
|
|
||||||
|
public enum PublicationStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Default Status. Publication is currently in progress
|
||||||
|
/// </summary>
|
||||||
|
[Description("On Going")]
|
||||||
|
OnGoing = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// Series is on temp or indefinite Hiatus
|
||||||
|
/// </summary>
|
||||||
|
[Description("Hiatus")]
|
||||||
|
Hiatus = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// Publication has finished releasing
|
||||||
|
/// </summary>
|
||||||
|
[Description("Completed")]
|
||||||
|
Completed = 2
|
||||||
|
|
||||||
|
}
|
@ -14,7 +14,6 @@ namespace API.Entities.Metadata
|
|||||||
|
|
||||||
public string Summary { get; set; }
|
public string Summary { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public ICollection<CollectionTag> CollectionTags { get; set; }
|
public ICollection<CollectionTag> CollectionTags { get; set; }
|
||||||
|
|
||||||
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
|
public ICollection<Genre> Genres { get; set; } = new List<Genre>();
|
||||||
@ -36,6 +35,11 @@ namespace API.Entities.Metadata
|
|||||||
/// Language of the content (ISO 639-1 code)
|
/// Language of the content (ISO 639-1 code)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Language { get; set; } = string.Empty;
|
public string Language { get; set; } = string.Empty;
|
||||||
|
/// <summary>
|
||||||
|
/// Total number of issues in the series
|
||||||
|
/// </summary>
|
||||||
|
public int Count { get; set; } = 0;
|
||||||
|
public PublicationStatus PublicationStatus { get; set; }
|
||||||
|
|
||||||
// Relationship
|
// Relationship
|
||||||
public Series Series { get; set; }
|
public Series Series { get; set; }
|
||||||
|
@ -107,6 +107,19 @@ public class MetadataService : IMetadataService
|
|||||||
chapter.Language = comicInfo.LanguageISO;
|
chapter.Language = comicInfo.LanguageISO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comicInfo.Count > 0)
|
||||||
|
{
|
||||||
|
chapter.TotalCount = comicInfo.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int.Parse(comicInfo.Number) > 0)
|
||||||
|
{
|
||||||
|
chapter.Count = int.Parse(comicInfo.Number);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (comicInfo.Year > 0)
|
if (comicInfo.Year > 0)
|
||||||
{
|
{
|
||||||
var day = Math.Max(comicInfo.Day, 1);
|
var day = Math.Max(comicInfo.Day, 1);
|
||||||
@ -295,6 +308,13 @@ public class MetadataService : IMetadataService
|
|||||||
series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating);
|
series.Metadata.AgeRating = chapters.Max(chapter => chapter.AgeRating);
|
||||||
|
|
||||||
|
|
||||||
|
series.Metadata.Count = chapters.Max(chapter => chapter.TotalCount);
|
||||||
|
series.Metadata.PublicationStatus = PublicationStatus.OnGoing;
|
||||||
|
if (chapters.Max(chapter => chapter.Count) >= series.Metadata.Count)
|
||||||
|
{
|
||||||
|
series.Metadata.PublicationStatus = PublicationStatus.Completed;
|
||||||
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(firstChapter.Summary))
|
if (!string.IsNullOrEmpty(firstChapter.Summary))
|
||||||
{
|
{
|
||||||
series.Metadata.Summary = firstChapter.Summary;
|
series.Metadata.Summary = firstChapter.Summary;
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
import { PublicationStatus } from "./publication-status";
|
||||||
|
|
||||||
|
export interface PublicationStatusDto {
|
||||||
|
value: PublicationStatus;
|
||||||
|
title: string;
|
||||||
|
}
|
5
UI/Web/src/app/_models/metadata/publication-status.ts
Normal file
5
UI/Web/src/app/_models/metadata/publication-status.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum PublicationStatus {
|
||||||
|
OnGoing = 0,
|
||||||
|
Hiatus = 1,
|
||||||
|
Completed = 2
|
||||||
|
}
|
@ -27,6 +27,7 @@ export interface SeriesFilter {
|
|||||||
sortOptions: SortOptions | null;
|
sortOptions: SortOptions | null;
|
||||||
tags: Array<number>;
|
tags: Array<number>;
|
||||||
languages: Array<string>;
|
languages: Array<string>;
|
||||||
|
publicationStatus: Array<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SortOptions {
|
export interface SortOptions {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { CollectionTag } from "./collection-tag";
|
import { CollectionTag } from "./collection-tag";
|
||||||
import { Genre } from "./genre";
|
import { Genre } from "./genre";
|
||||||
import { AgeRating } from "./metadata/age-rating";
|
import { AgeRating } from "./metadata/age-rating";
|
||||||
|
import { PublicationStatus } from "./metadata/publication-status";
|
||||||
import { Person } from "./person";
|
import { Person } from "./person";
|
||||||
import { Tag } from "./tag";
|
import { Tag } from "./tag";
|
||||||
|
|
||||||
@ -24,4 +25,5 @@ export interface SeriesMetadata {
|
|||||||
releaseYear: number;
|
releaseYear: number;
|
||||||
language: string;
|
language: string;
|
||||||
seriesId: number;
|
seriesId: number;
|
||||||
|
publicationStatus: PublicationStatus;
|
||||||
}
|
}
|
@ -8,6 +8,7 @@ import { Genre } from '../_models/genre';
|
|||||||
import { AgeRating } from '../_models/metadata/age-rating';
|
import { AgeRating } from '../_models/metadata/age-rating';
|
||||||
import { AgeRatingDto } from '../_models/metadata/age-rating-dto';
|
import { AgeRatingDto } from '../_models/metadata/age-rating-dto';
|
||||||
import { Language } from '../_models/metadata/language';
|
import { Language } from '../_models/metadata/language';
|
||||||
|
import { PublicationStatusDto } from '../_models/metadata/publication-status-dto';
|
||||||
import { Person } from '../_models/person';
|
import { Person } from '../_models/person';
|
||||||
import { Tag } from '../_models/tag';
|
import { Tag } from '../_models/tag';
|
||||||
|
|
||||||
@ -44,6 +45,14 @@ export class MetadataService {
|
|||||||
return this.httpClient.get<Array<AgeRatingDto>>(this.baseUrl + method);;
|
return this.httpClient.get<Array<AgeRatingDto>>(this.baseUrl + method);;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllPublicationStatus(libraries?: Array<number>) {
|
||||||
|
let method = 'metadata/publication-status'
|
||||||
|
if (libraries != undefined && libraries.length > 0) {
|
||||||
|
method += '?libraryIds=' + libraries.join(',');
|
||||||
|
}
|
||||||
|
return this.httpClient.get<Array<PublicationStatusDto>>(this.baseUrl + method);;
|
||||||
|
}
|
||||||
|
|
||||||
getAllTags(libraries?: Array<number>) {
|
getAllTags(libraries?: Array<number>) {
|
||||||
let method = 'metadata/tags'
|
let method = 'metadata/tags'
|
||||||
if (libraries != undefined && libraries.length > 0) {
|
if (libraries != undefined && libraries.length > 0) {
|
||||||
|
@ -212,7 +212,8 @@ export class SeriesService {
|
|||||||
sortOptions: null,
|
sortOptions: null,
|
||||||
ageRating: [],
|
ageRating: [],
|
||||||
tags: [],
|
tags: [],
|
||||||
languages: []
|
languages: [],
|
||||||
|
publicationStatus: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (filter === undefined) return data;
|
if (filter === undefined) return data;
|
||||||
|
@ -35,6 +35,7 @@ import { NavEventsToggleComponent } from './nav-events-toggle/nav-events-toggle.
|
|||||||
import { PersonRolePipe } from './person-role.pipe';
|
import { PersonRolePipe } from './person-role.pipe';
|
||||||
import { SeriesMetadataDetailComponent } from './series-metadata-detail/series-metadata-detail.component';
|
import { SeriesMetadataDetailComponent } from './series-metadata-detail/series-metadata-detail.component';
|
||||||
import { AllSeriesComponent } from './all-series/all-series.component';
|
import { AllSeriesComponent } from './all-series/all-series.component';
|
||||||
|
import { PublicationStatusPipe } from './publication-status.pipe';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -52,6 +53,7 @@ import { AllSeriesComponent } from './all-series/all-series.component';
|
|||||||
DashboardComponent,
|
DashboardComponent,
|
||||||
NavEventsToggleComponent,
|
NavEventsToggleComponent,
|
||||||
PersonRolePipe,
|
PersonRolePipe,
|
||||||
|
PublicationStatusPipe,
|
||||||
SeriesMetadataDetailComponent,
|
SeriesMetadataDetailComponent,
|
||||||
AllSeriesComponent,
|
AllSeriesComponent,
|
||||||
],
|
],
|
||||||
|
@ -309,6 +309,19 @@
|
|||||||
</app-typeahead>
|
</app-typeahead>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-2 mr-3" *ngIf="!filterSettings.publicationStatusDisabled">
|
||||||
|
<label for="publication-status">Publication Status</label>
|
||||||
|
<app-typeahead (selectedData)="updatePublicationStatus($event)" [settings]="publicationStatusSettings" [reset]="resetTypeaheads">
|
||||||
|
<ng-template #badgeItem let-item let-position="idx">
|
||||||
|
{{item.title}}
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #optionItem let-item let-position="idx">
|
||||||
|
{{item.title}}
|
||||||
|
</ng-template>
|
||||||
|
</app-typeahead>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center no-gutters">
|
||||||
<div class="col-md-2 mr-3" *ngIf="!filterSettings.sortDisabled">
|
<div class="col-md-2 mr-3" *ngIf="!filterSettings.sortDisabled">
|
||||||
<form [formGroup]="sortGroup">
|
<form [formGroup]="sortGroup">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -326,16 +339,13 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-2 mr-3"></div>
|
||||||
|
<div class="col-md-2 mr-3"></div>
|
||||||
|
<div class="col-md-2 mr-3 mt-4">
|
||||||
</div>
|
|
||||||
<div class="row justify-content-center no-gutters">
|
|
||||||
<div class="col-md-3 mr-3">
|
|
||||||
<button class="btn btn-secondary btn-block" (click)="clear()">Clear</button>
|
<button class="btn btn-secondary btn-block" (click)="clear()">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-2 mr-3 mt-4">
|
||||||
<button class="btn btn-primary btn-block" (click)="apply()">Apply</button>
|
<button class="btn btn-primary btn-block" (click)="apply()">Apply</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,7 @@ import { MangaFormat } from 'src/app/_models/manga-format';
|
|||||||
import { AgeRating } from 'src/app/_models/metadata/age-rating';
|
import { AgeRating } from 'src/app/_models/metadata/age-rating';
|
||||||
import { AgeRatingDto } from 'src/app/_models/metadata/age-rating-dto';
|
import { AgeRatingDto } from 'src/app/_models/metadata/age-rating-dto';
|
||||||
import { Language } from 'src/app/_models/metadata/language';
|
import { Language } from 'src/app/_models/metadata/language';
|
||||||
|
import { PublicationStatusDto } from 'src/app/_models/metadata/publication-status-dto';
|
||||||
import { Pagination } from 'src/app/_models/pagination';
|
import { Pagination } from 'src/app/_models/pagination';
|
||||||
import { Person, PersonRole } from 'src/app/_models/person';
|
import { Person, PersonRole } from 'src/app/_models/person';
|
||||||
import { FilterItem, mangaFormatFilters, SeriesFilter, SortField } from 'src/app/_models/series-filter';
|
import { FilterItem, mangaFormatFilters, SeriesFilter, SortField } from 'src/app/_models/series-filter';
|
||||||
@ -39,6 +40,7 @@ export class FilterSettings {
|
|||||||
ageRatingDisabled = false;
|
ageRatingDisabled = false;
|
||||||
tagsDisabled = false;
|
tagsDisabled = false;
|
||||||
languageDisabled = false;
|
languageDisabled = false;
|
||||||
|
publicationStatusDisabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -70,6 +72,7 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
genreSettings: TypeaheadSettings<FilterItem<Genre>> = new TypeaheadSettings();
|
genreSettings: TypeaheadSettings<FilterItem<Genre>> = new TypeaheadSettings();
|
||||||
collectionSettings: TypeaheadSettings<FilterItem<CollectionTag>> = new TypeaheadSettings();
|
collectionSettings: TypeaheadSettings<FilterItem<CollectionTag>> = new TypeaheadSettings();
|
||||||
ageRatingSettings: TypeaheadSettings<FilterItem<AgeRatingDto>> = new TypeaheadSettings();
|
ageRatingSettings: TypeaheadSettings<FilterItem<AgeRatingDto>> = new TypeaheadSettings();
|
||||||
|
publicationStatusSettings: TypeaheadSettings<FilterItem<PublicationStatusDto>> = new TypeaheadSettings();
|
||||||
tagsSettings: TypeaheadSettings<FilterItem<Tag>> = new TypeaheadSettings();
|
tagsSettings: TypeaheadSettings<FilterItem<Tag>> = new TypeaheadSettings();
|
||||||
languageSettings: TypeaheadSettings<FilterItem<Language>> = new TypeaheadSettings();
|
languageSettings: TypeaheadSettings<FilterItem<Language>> = new TypeaheadSettings();
|
||||||
peopleSettings: {[PersonRole: string]: TypeaheadSettings<FilterItem<Person>>} = {};
|
peopleSettings: {[PersonRole: string]: TypeaheadSettings<FilterItem<Person>>} = {};
|
||||||
@ -84,7 +87,7 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
libraries: Array<FilterItem<Library>> = [];
|
libraries: Array<FilterItem<Library>> = [];
|
||||||
genres: Array<FilterItem<Genre>> = [];
|
genres: Array<FilterItem<Genre>> = [];
|
||||||
persons: Array<FilterItem<Person>> = [];
|
persons: Array<FilterItem<Person>> = [];
|
||||||
//collectionTags: Array<FilterItem<CollectionTag>> = [];
|
|
||||||
|
|
||||||
readProgressGroup!: FormGroup;
|
readProgressGroup!: FormGroup;
|
||||||
sortGroup!: FormGroup;
|
sortGroup!: FormGroup;
|
||||||
@ -171,6 +174,7 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
this.setupCollectionTagTypeahead();
|
this.setupCollectionTagTypeahead();
|
||||||
this.setupPersonTypeahead();
|
this.setupPersonTypeahead();
|
||||||
this.setupAgeRatingSettings();
|
this.setupAgeRatingSettings();
|
||||||
|
this.setupPublicationStatusSettings();
|
||||||
this.setupTagSettings();
|
this.setupTagSettings();
|
||||||
this.setupLanguageSettings();
|
this.setupLanguageSettings();
|
||||||
}
|
}
|
||||||
@ -261,6 +265,29 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupPublicationStatusSettings() {
|
||||||
|
this.publicationStatusSettings.minCharacters = 0;
|
||||||
|
this.publicationStatusSettings.multiple = true;
|
||||||
|
this.publicationStatusSettings.id = 'publication-status';
|
||||||
|
this.publicationStatusSettings.unique = true;
|
||||||
|
this.publicationStatusSettings.addIfNonExisting = false;
|
||||||
|
this.publicationStatusSettings.fetchFn = (filter: string) => {
|
||||||
|
return this.metadataService.getAllPublicationStatus(this.filter.libraries).pipe(map(statuses => {
|
||||||
|
return statuses.map(status => {
|
||||||
|
return {
|
||||||
|
title: status.title,
|
||||||
|
value: status,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
this.publicationStatusSettings.compareFn = (options: FilterItem<PublicationStatusDto>[], filter: string) => {
|
||||||
|
const f = filter.toLowerCase();
|
||||||
|
return options.filter(m => m.title.toLowerCase() === f && this.utilityService.filter(m.title, filter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setupTagSettings() {
|
setupTagSettings() {
|
||||||
this.tagsSettings.minCharacters = 0;
|
this.tagsSettings.minCharacters = 0;
|
||||||
this.tagsSettings.multiple = true;
|
this.tagsSettings.multiple = true;
|
||||||
@ -475,9 +502,6 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
case PersonRole.Colorist:
|
case PersonRole.Colorist:
|
||||||
this.filter.colorist = persons.map(p => p.value.id);
|
this.filter.colorist = persons.map(p => p.value.id);
|
||||||
break;
|
break;
|
||||||
// case PersonRole.Artist:
|
|
||||||
// this.filter.artist = persons.map(p => p.value.id);
|
|
||||||
// break;
|
|
||||||
case PersonRole.Editor:
|
case PersonRole.Editor:
|
||||||
this.filter.editor = persons.map(p => p.value.id);
|
this.filter.editor = persons.map(p => p.value.id);
|
||||||
break;
|
break;
|
||||||
@ -514,12 +538,15 @@ export class CardDetailLayoutComponent implements OnInit, OnDestroy {
|
|||||||
this.filter.ageRating = ratingDtos.map(item => item.value.value) || [];
|
this.filter.ageRating = ratingDtos.map(item => item.value.value) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePublicationStatus(dtos: FilterItem<PublicationStatusDto>[]) {
|
||||||
|
this.filter.publicationStatus = dtos.map(item => item.value.value) || [];
|
||||||
|
}
|
||||||
|
|
||||||
updateLanguageRating(languages: FilterItem<Language>[]) {
|
updateLanguageRating(languages: FilterItem<Language>[]) {
|
||||||
this.filter.languages = languages.map(item => item.value.isoCode) || [];
|
this.filter.languages = languages.map(item => item.value.isoCode) || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
updateReadStatus(status: string) {
|
updateReadStatus(status: string) {
|
||||||
console.log('readstatus: ', this.filter.readStatus);
|
|
||||||
if (status === 'read') {
|
if (status === 'read') {
|
||||||
this.filter.readStatus.read = !this.filter.readStatus.read;
|
this.filter.readStatus.read = !this.filter.readStatus.read;
|
||||||
} else if (status === 'inProgress') {
|
} else if (status === 'inProgress') {
|
||||||
|
19
UI/Web/src/app/publication-status.pipe.ts
Normal file
19
UI/Web/src/app/publication-status.pipe.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { PublicationStatus } from './_models/metadata/publication-status';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'publicationStatus'
|
||||||
|
})
|
||||||
|
export class PublicationStatusPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: PublicationStatus): string {
|
||||||
|
switch (value) {
|
||||||
|
case PublicationStatus.OnGoing: return 'Ongoing';
|
||||||
|
case PublicationStatus.Hiatus: return 'Hiatus';
|
||||||
|
case PublicationStatus.Completed: return 'Completed';
|
||||||
|
|
||||||
|
default: return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -10,6 +10,7 @@
|
|||||||
<!-- tooltip here explaining how this is year of first issue -->
|
<!-- tooltip here explaining how this is year of first issue -->
|
||||||
<app-tag-badge *ngIf="seriesMetadata.releaseYear > 0" title="Release date">{{seriesMetadata.releaseYear}}</app-tag-badge>
|
<app-tag-badge *ngIf="seriesMetadata.releaseYear > 0" title="Release date">{{seriesMetadata.releaseYear}}</app-tag-badge>
|
||||||
<app-tag-badge *ngIf="seriesMetadata.language !== null && seriesMetadata.language !== ''" title="Language">{{seriesMetadata.language}}</app-tag-badge>
|
<app-tag-badge *ngIf="seriesMetadata.language !== null && seriesMetadata.language !== ''" title="Language">{{seriesMetadata.language}}</app-tag-badge>
|
||||||
|
<app-tag-badge title="Publication Status">{{seriesMetadata.publicationStatus | publicationStatus}}</app-tag-badge>
|
||||||
<app-tag-badge [selectionMode]="TagBadgeCursor.NotAllowed">
|
<app-tag-badge [selectionMode]="TagBadgeCursor.NotAllowed">
|
||||||
<app-series-format [format]="series.format">{{utilityService.mangaFormat(series.format)}}</app-series-format>
|
<app-series-format [format]="series.format">{{utilityService.mangaFormat(series.format)}}</app-series-format>
|
||||||
</app-tag-badge>
|
</app-tag-badge>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user