OPDS-PS v1.2 Support + a few bugfixes (#1869)
* Fixed up a localization lookup test case * Refactored some webp to a unified method * Cleaned up some code * Expanded webp conversion for covers to all entities * Code cleanup * Prompt the user when they are about to delete multiple series via bulk actions * Aligned Kavita to OPDS-PS 1.2. * Fixed a bug where clearing metadata filter of series name didn't clear the actual field. * Added some documentation * Refactored how covert covers to webp works. Now we will handle all custom covers for all entities. Volumes and Series will not be touched but instead be updated via a RefreshCovers call. This will fix up the references much faster. * Fixed up the OPDS-PS 1.2 attributes to only show on PS links
This commit is contained in:
parent
1f34068662
commit
47269b4c51
30 changed files with 334 additions and 99 deletions
|
@ -33,6 +33,7 @@ public interface ICollectionTagRepository
|
|||
Task<IEnumerable<CollectionTag>> GetAllTagsAsync(CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<bool> TagExists(string title);
|
||||
Task<IList<CollectionTag>> GetAllWithNonWebPCovers();
|
||||
}
|
||||
public class CollectionTagRepository : ICollectionTagRepository
|
||||
{
|
||||
|
@ -106,6 +107,13 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
.AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized));
|
||||
}
|
||||
|
||||
public async Task<IList<CollectionTag>> GetAllWithNonWebPCovers()
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage) && !c.CoverImage.EndsWith(".webp"))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync()
|
||||
{
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ public interface ILibraryRepository
|
|||
Task<string?> GetLibraryCoverImageAsync(int libraryId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<IDictionary<int, LibraryType>> GetLibraryTypesForIdsAsync(IEnumerable<int> libraryIds);
|
||||
Task<IList<Library>> GetAllWithNonWebPCovers();
|
||||
}
|
||||
|
||||
public class LibraryRepository : ILibraryRepository
|
||||
|
@ -368,4 +369,11 @@ public class LibraryRepository : ILibraryRepository
|
|||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public async Task<IList<Library>> GetAllWithNonWebPCovers()
|
||||
{
|
||||
return await _context.Library
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage) && !c.CoverImage.EndsWith(".webp"))
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ public interface IReadingListRepository
|
|||
Task<bool> ReadingListExists(string name);
|
||||
Task<List<ReadingList>> GetAllReadingListsAsync();
|
||||
IEnumerable<PersonDto> GetReadingListCharactersAsync(int readingListId);
|
||||
Task<IList<ReadingList>> GetAllWithNonWebPCovers();
|
||||
}
|
||||
|
||||
public class ReadingListRepository : IReadingListRepository
|
||||
|
@ -106,6 +107,13 @@ public class ReadingListRepository : IReadingListRepository
|
|||
.AsEnumerable();
|
||||
}
|
||||
|
||||
public async Task<IList<ReadingList>> GetAllWithNonWebPCovers()
|
||||
{
|
||||
return await _context.ReadingList
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage) && !c.CoverImage.EndsWith(".webp"))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public void Remove(ReadingListItem item)
|
||||
{
|
||||
_context.ReadingListItem.Remove(item);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -130,6 +131,7 @@ public interface ISeriesRepository
|
|||
Task<IDictionary<int, int>> GetLibraryIdsForSeriesAsync();
|
||||
|
||||
Task<IList<SeriesMetadataDto>> GetSeriesMetadataForIds(IEnumerable<int> seriesIds);
|
||||
Task<IList<Series>> GetAllWithNonWebPCovers(bool customOnly = true);
|
||||
}
|
||||
|
||||
public class SeriesRepository : ISeriesRepository
|
||||
|
@ -560,6 +562,21 @@ public class SeriesRepository : ISeriesRepository
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns custom images only
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IList<Series>> GetAllWithNonWebPCovers(bool customOnly = true)
|
||||
{
|
||||
var prefix = ImageService.GetSeriesFormat(0).Replace("0", string.Empty);
|
||||
return await _context.Series
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage)
|
||||
&& !c.CoverImage.EndsWith(".webp")
|
||||
&& (!customOnly || c.CoverImage.StartsWith(prefix)))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task AddSeriesModifiers(int userId, List<SeriesDto> series)
|
||||
{
|
||||
var userProgress = await _context.AppUserProgresses
|
||||
|
@ -1262,38 +1279,40 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <param name="format"></param>
|
||||
/// <param name="withFullIncludes">Defaults to true. This will query against all foreign keys (deep). If false, just the series will come back</param>
|
||||
/// <returns></returns>
|
||||
public Task<Series?> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true)
|
||||
public Task<Series?> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId,
|
||||
MangaFormat format, bool withFullIncludes = true)
|
||||
{
|
||||
var normalizedSeries = seriesName.ToNormalized();
|
||||
var normalizedLocalized = localizedName.ToNormalized();
|
||||
var query = _context.Series
|
||||
.Where(s => s.LibraryId == libraryId)
|
||||
.Where(s => s.Format == format && format != MangaFormat.Unknown)
|
||||
.Where(s => s.NormalizedName.Equals(normalizedSeries)
|
||||
|| (s.NormalizedLocalizedName == normalizedSeries)
|
||||
|| (s.OriginalName == seriesName));
|
||||
.Where(s =>
|
||||
s.NormalizedName.Equals(normalizedSeries)
|
||||
|| s.NormalizedName.Equals(normalizedLocalized)
|
||||
|
||||
if (!string.IsNullOrEmpty(normalizedLocalized))
|
||||
{
|
||||
// TODO: Apply WhereIf
|
||||
query = query.Where(s =>
|
||||
s.NormalizedName.Equals(normalizedLocalized)
|
||||
|| (s.NormalizedLocalizedName != null && s.NormalizedLocalizedName.Equals(normalizedLocalized)));
|
||||
}
|
||||
|| s.NormalizedLocalizedName.Equals(normalizedSeries)
|
||||
|| (!string.IsNullOrEmpty(normalizedLocalized) && s.NormalizedLocalizedName.Equals(normalizedLocalized))
|
||||
|
||||
|| (s.OriginalName != null && s.OriginalName.Equals(seriesName))
|
||||
);
|
||||
if (!withFullIncludes)
|
||||
{
|
||||
return query.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
return query.Include(s => s.Metadata)
|
||||
query = query.Include(s => s.Library)
|
||||
|
||||
.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.People)
|
||||
|
||||
.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.Genres)
|
||||
|
||||
.Include(s => s.Library)
|
||||
.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.Tags)
|
||||
|
||||
.Include(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(cm => cm.People)
|
||||
|
@ -1306,15 +1325,12 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(c => c.Genres)
|
||||
|
||||
|
||||
.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.Tags)
|
||||
|
||||
.Include(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
.AsSplitQuery();
|
||||
return query.SingleOrDefaultAsync();
|
||||
#nullable enable
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
|||
using API.DTOs;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -24,6 +25,7 @@ public interface IVolumeRepository
|
|||
Task<IEnumerable<Volume>> GetVolumesForSeriesAsync(IList<int> seriesIds, bool includeChapters = false);
|
||||
Task<IEnumerable<Volume>> GetVolumes(int seriesId);
|
||||
Task<Volume?> GetVolumeByIdAsync(int volumeId);
|
||||
Task<IList<Volume>> GetAllWithNonWebPCovers();
|
||||
}
|
||||
public class VolumeRepository : IVolumeRepository
|
||||
{
|
||||
|
@ -195,6 +197,13 @@ public class VolumeRepository : IVolumeRepository
|
|||
return await _context.Volume.SingleOrDefaultAsync(x => x.Id == volumeId);
|
||||
}
|
||||
|
||||
public async Task<IList<Volume>> GetAllWithNonWebPCovers()
|
||||
{
|
||||
return await _context.Volume
|
||||
.Where(c => !string.IsNullOrEmpty(c.CoverImage) && !c.CoverImage.EndsWith(".webp"))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
private static void SortSpecialChapters(IEnumerable<VolumeDto> volumes)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue