Tachiyomi Enhancements (#845)
* Added a new endpoint to get all Series with Progress info. * Fixed up some potential NPEs during scan * Commented out filter code, not ready for it. * Fixed up a parsing case for european comics * Refactored FilterDto to allow for specifying multiple formats to return. * Refactored FilterDto to allow for specifying multiple formats to return. * Refactored the UI to show OPDS as 3rd Party Clients since Tachiyomi now uses OPDS url scheme for authentication.
This commit is contained in:
parent
658ca290e1
commit
384ebcef5c
20 changed files with 120 additions and 99 deletions
|
@ -11,6 +11,7 @@ using API.DTOs.CollectionTags;
|
|||
using API.DTOs.Filtering;
|
||||
using API.DTOs.OPDS;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
|
@ -33,7 +34,7 @@ public class OpdsController : BaseApiController
|
|||
private const string Prefix = "/api/opds/";
|
||||
private readonly FilterDto _filterDto = new FilterDto()
|
||||
{
|
||||
MangaFormat = null
|
||||
Formats = new List<MangaFormat>()
|
||||
};
|
||||
private readonly ChapterSortComparer _chapterSortComparer = new ChapterSortComparer();
|
||||
|
||||
|
|
|
@ -233,6 +233,23 @@ namespace API.Controllers
|
|||
return Ok(series);
|
||||
}
|
||||
|
||||
[HttpPost("all")]
|
||||
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetAllSeries(FilterDto filterDto, [FromQuery] UserParams userParams, [FromQuery] int libraryId = 0)
|
||||
{
|
||||
var userId = await _unitOfWork.UserRepository.GetUserIdByUsernameAsync(User.GetUsername());
|
||||
var series =
|
||||
await _unitOfWork.SeriesRepository.GetSeriesDtoForLibraryIdAsync(libraryId, userId, userParams, filterDto);
|
||||
|
||||
// Apply progress/rating information (I can't work out how to do this in initial query)
|
||||
if (series == null) return BadRequest("Could not get series");
|
||||
|
||||
await _unitOfWork.SeriesRepository.AddSeriesModifiers(userId, series);
|
||||
|
||||
Response.AddPaginationHeader(series.CurrentPage, series.PageSize, series.TotalCount, series.TotalPages);
|
||||
|
||||
return Ok(series);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches series that are on deck aka have progress on them.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
using API.Entities.Enums;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using API.Entities.Enums;
|
||||
|
||||
namespace API.DTOs.Filtering
|
||||
{
|
||||
public class FilterDto
|
||||
{
|
||||
/// <summary>
|
||||
/// Pass null if you want all formats
|
||||
/// The type of Formats you want to be returned. An empty list will return all formats back
|
||||
/// </summary>
|
||||
public MangaFormat? MangaFormat { get; init; } = null;
|
||||
public IList<MangaFormat> Formats { get; init; } = new List<MangaFormat>();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,20 +11,20 @@ namespace API.Data.Metadata
|
|||
/// <remarks>See reference of the loose spec here: https://github.com/Kussie/ComicInfoStandard/blob/main/ComicInfo.xsd</remarks>
|
||||
public class ComicInfo
|
||||
{
|
||||
public string Summary { get; set; }
|
||||
public string Title { get; set; }
|
||||
public string Series { get; set; }
|
||||
public string Number { get; set; }
|
||||
public string Volume { get; set; }
|
||||
public string Notes { get; set; }
|
||||
public string Genre { get; set; }
|
||||
public string Summary { get; set; } = string.Empty;
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Series { get; set; } = string.Empty;
|
||||
public string Number { get; set; } = string.Empty;
|
||||
public string Volume { get; set; } = string.Empty;
|
||||
public string Notes { get; set; } = string.Empty;
|
||||
public string Genre { get; set; } = string.Empty;
|
||||
public int PageCount { get; set; }
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public string LanguageISO { get; set; }
|
||||
public string LanguageISO { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// This is the link to where the data was scraped from
|
||||
/// </summary>
|
||||
public string Web { get; set; }
|
||||
public string Web { get; set; } = string.Empty;
|
||||
public int Day { get; set; }
|
||||
public int Month { get; set; }
|
||||
public int Year { get; set; }
|
||||
|
@ -33,29 +33,23 @@ namespace API.Data.Metadata
|
|||
/// <summary>
|
||||
/// Rating based on the content. Think PG-13, R for movies. See <see cref="AgeRating"/> for valid types
|
||||
/// </summary>
|
||||
public string AgeRating { get; set; }
|
||||
|
||||
// public AgeRating AgeRating
|
||||
// {
|
||||
// get => ConvertAgeRatingToEnum(_AgeRating);
|
||||
// set => ConvertAgeRatingToEnum(value);
|
||||
// }
|
||||
public string AgeRating { get; set; } = string.Empty;
|
||||
/// <summary>
|
||||
/// User's rating of the content
|
||||
/// </summary>
|
||||
public float UserRating { get; set; }
|
||||
|
||||
public string AlternateSeries { get; set; }
|
||||
public string StoryArc { get; set; }
|
||||
public string SeriesGroup { get; set; }
|
||||
public string AlternativeSeries { get; set; }
|
||||
public string AlternativeNumber { get; set; }
|
||||
public string AlternateSeries { get; set; } = string.Empty;
|
||||
public string StoryArc { get; set; } = string.Empty;
|
||||
public string SeriesGroup { get; set; } = string.Empty;
|
||||
public string AlternativeSeries { get; set; } = string.Empty;
|
||||
public string AlternativeNumber { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// This is Epub only: calibre:title_sort
|
||||
/// Represents the sort order for the title
|
||||
/// </summary>
|
||||
public string TitleSort { get; set; }
|
||||
public string TitleSort { get; set; } = string.Empty;
|
||||
|
||||
|
||||
|
||||
|
@ -63,14 +57,14 @@ namespace API.Data.Metadata
|
|||
/// <summary>
|
||||
/// This is the Author. For Books, we map creator tag in OPF to this field. Comma separated if multiple.
|
||||
/// </summary>
|
||||
public string Writer { get; set; }
|
||||
public string Penciller { get; set; }
|
||||
public string Inker { get; set; }
|
||||
public string Colorist { get; set; }
|
||||
public string Letterer { get; set; }
|
||||
public string CoverArtist { get; set; }
|
||||
public string Editor { get; set; }
|
||||
public string Publisher { get; set; }
|
||||
public string Writer { get; set; } = string.Empty;
|
||||
public string Penciller { get; set; } = string.Empty;
|
||||
public string Inker { get; set; } = string.Empty;
|
||||
public string Colorist { get; set; } = string.Empty;
|
||||
public string Letterer { get; set; } = string.Empty;
|
||||
public string CoverArtist { get; set; } = string.Empty;
|
||||
public string Editor { get; set; } = string.Empty;
|
||||
public string Publisher { get; set; } = string.Empty;
|
||||
|
||||
public static AgeRating ConvertAgeRatingToEnum(string value)
|
||||
{
|
||||
|
|
|
@ -178,8 +178,11 @@ public class SeriesRepository : ISeriesRepository
|
|||
public async Task<PagedList<SeriesDto>> GetSeriesDtoForLibraryIdAsync(int libraryId, int userId, UserParams userParams, FilterDto filter)
|
||||
{
|
||||
var formats = filter.GetSqlFilter();
|
||||
|
||||
var userLibraries = await GetUserLibraries(libraryId, userId);
|
||||
|
||||
var query = _context.Series
|
||||
.Where(s => s.LibraryId == libraryId && formats.Contains(s.Format))
|
||||
.Where(s => userLibraries.Contains(s.LibraryId) && formats.Contains(s.Format))
|
||||
.OrderBy(s => s.SortName)
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.AsNoTracking();
|
||||
|
@ -187,6 +190,24 @@ public class SeriesRepository : ISeriesRepository
|
|||
return await PagedList<SeriesDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
private async Task<List<int>> GetUserLibraries(int libraryId, int userId)
|
||||
{
|
||||
if (libraryId == 0)
|
||||
{
|
||||
return await _context.Library
|
||||
.Include(l => l.AppUsers)
|
||||
.Where(library => library.AppUsers.Any(user => user.Id == userId))
|
||||
.AsNoTracking()
|
||||
.Select(library => library.Id)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
return new List<int>()
|
||||
{
|
||||
libraryId
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SearchResultDto>> SearchSeries(int[] libraryIds, string searchQuery)
|
||||
{
|
||||
return await _context.Series
|
||||
|
@ -357,26 +378,10 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
var formats = filter.GetSqlFilter();
|
||||
|
||||
if (libraryId == 0)
|
||||
{
|
||||
var userLibraries = _context.Library
|
||||
.Include(l => l.AppUsers)
|
||||
.Where(library => library.AppUsers.Any(user => user.Id == userId))
|
||||
.AsNoTracking()
|
||||
.Select(library => library.Id)
|
||||
.ToList();
|
||||
|
||||
var allQuery = _context.Series
|
||||
.Where(s => userLibraries.Contains(s.LibraryId) && formats.Contains(s.Format))
|
||||
.OrderByDescending(s => s.Created)
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.AsNoTracking();
|
||||
|
||||
return await PagedList<SeriesDto>.CreateAsync(allQuery, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
var userLibraries = await GetUserLibraries(libraryId, userId);
|
||||
|
||||
var query = _context.Series
|
||||
.Where(s => s.LibraryId == libraryId && formats.Contains(s.Format))
|
||||
.Where(s => userLibraries.Contains(s.LibraryId) && formats.Contains(s.Format))
|
||||
.OrderByDescending(s => s.Created)
|
||||
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||
.AsSplitQuery()
|
||||
|
@ -397,20 +402,8 @@ public class SeriesRepository : ISeriesRepository
|
|||
public async Task<IEnumerable<SeriesDto>> GetOnDeck(int userId, int libraryId, UserParams userParams, FilterDto filter)
|
||||
{
|
||||
var formats = filter.GetSqlFilter();
|
||||
IList<int> userLibraries;
|
||||
if (libraryId == 0)
|
||||
{
|
||||
userLibraries = _context.Library
|
||||
.Include(l => l.AppUsers)
|
||||
.Where(library => library.AppUsers.Any(user => user.Id == userId))
|
||||
.AsNoTracking()
|
||||
.Select(library => library.Id)
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
userLibraries = new List<int>() {libraryId};
|
||||
}
|
||||
|
||||
var userLibraries = await GetUserLibraries(libraryId, userId);
|
||||
|
||||
var series = _context.Series
|
||||
.Where(s => formats.Contains(s.Format) && userLibraries.Contains(s.LibraryId))
|
||||
|
|
|
@ -11,15 +11,12 @@ namespace API.Extensions
|
|||
|
||||
public static IList<MangaFormat> GetSqlFilter(this FilterDto filter)
|
||||
{
|
||||
var format = filter.MangaFormat;
|
||||
if (format != null)
|
||||
if (filter.Formats == null || filter.Formats.Count == 0)
|
||||
{
|
||||
return new List<MangaFormat>()
|
||||
{
|
||||
(MangaFormat) format
|
||||
};
|
||||
return AllFormats;
|
||||
}
|
||||
return AllFormats;
|
||||
|
||||
return filter.Formats;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,9 +237,13 @@ namespace API.Parser
|
|||
|
||||
private static readonly Regex[] ComicSeriesRegex = new[]
|
||||
{
|
||||
// Tintin - T22 Vol 714 pour Sydney
|
||||
new Regex(
|
||||
@"(?<Series>.+?)\s?(\b|_|-)\s?((vol|tome|t)\.?)(?<Volume>\d+(-\d+)?)",
|
||||
MatchOptions, RegexTimeout),
|
||||
// Invincible Vol 01 Family matters (2005) (Digital)
|
||||
new Regex(
|
||||
@"(?<Series>.*)(\b|_)((vol|tome|t)\.?)( |_)(?<Volume>\d+(-\d+)?)",
|
||||
@"(?<Series>.+?)(\b|_)((vol|tome|t)\.?)(\s|_)(?<Volume>\d+(-\d+)?)",
|
||||
MatchOptions, RegexTimeout),
|
||||
// Batman Beyond 2.0 001 (2013)
|
||||
new Regex(
|
||||
|
|
|
@ -287,7 +287,7 @@ public class MetadataService : IMetadataService
|
|||
series.Metadata.ReleaseYear = series.Volumes
|
||||
.SelectMany(volume => volume.Chapters).Min(c => c.ReleaseDate.Year);
|
||||
|
||||
var genres = comicInfos.SelectMany(i => i.Genre.Split(",")).Distinct().ToList();
|
||||
var genres = comicInfos.SelectMany(i => i?.Genre.Split(",")).Distinct().ToList();
|
||||
var people = series.Volumes.SelectMany(volume => volume.Chapters).SelectMany(c => c.People).ToList();
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"TokenKey": "super secret unguessable key",
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Default": "Debug",
|
||||
"Microsoft": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Error",
|
||||
"Hangfire": "Information",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue