New Scanner + People Pages (#3286)

Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
Joe Milazzo 2024-10-23 15:11:18 -07:00 committed by GitHub
parent 1ed0eae22d
commit ba20ad4ecc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
142 changed files with 17529 additions and 3038 deletions

View file

@ -24,6 +24,8 @@ public static class ApplicationServiceExtensions
{
services.AddAutoMapper(typeof(AutoMapperProfiles).Assembly);
//services.AddScoped<DataContext>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<ITokenService, TokenService>();
services.AddScoped<IFileService, FileService>();
@ -45,7 +47,6 @@ public static class ApplicationServiceExtensions
services.AddScoped<IBookmarkService, BookmarkService>();
services.AddScoped<IThemeService, ThemeService>();
services.AddScoped<ISeriesService, SeriesService>();
services.AddScoped<IProcessSeries, ProcessSeries>();
services.AddScoped<IReadingListService, ReadingListService>();
services.AddScoped<IDeviceService, DeviceService>();
services.AddScoped<IStatisticService, StatisticService>();
@ -55,12 +56,12 @@ public static class ApplicationServiceExtensions
services.AddScoped<IStreamService, StreamService>();
services.AddScoped<IScannerService, ScannerService>();
services.AddScoped<IProcessSeries, ProcessSeries>();
services.AddScoped<IMetadataService, MetadataService>();
services.AddScoped<IWordCountAnalyzerService, WordCountAnalyzerService>();
services.AddScoped<ILibraryWatcher, LibraryWatcher>();
services.AddScoped<ITachiyomiService, TachiyomiService>();
services.AddScoped<ICollectionTagService, CollectionTagService>();
services.AddScoped<ITagManagerService, TagManagerService>();
services.AddScoped<IFileSystem, FileSystem>();
services.AddScoped<IDirectoryService, DirectoryService>();

View file

@ -4,6 +4,7 @@ using API.Data.Misc;
using API.Data.Repositories;
using API.Entities;
using API.Entities.Metadata;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
namespace API.Extensions.QueryExtensions.Filtering;
@ -45,10 +46,25 @@ public static class SearchQueryableExtensions
public static IQueryable<Person> SearchPeople(this IQueryable<SeriesMetadata> queryable,
string searchQuery, IEnumerable<int> seriesIds)
{
return queryable
// Get people from SeriesMetadata
var peopleFromSeriesMetadata = queryable
.Where(sm => seriesIds.Contains(sm.SeriesId))
.SelectMany(sm => sm.People.Where(t => t.Name != null && EF.Functions.Like(t.Name, $"%{searchQuery}%")))
.AsSplitQuery()
.SelectMany(sm => sm.People)
.Where(p => p.Person.Name != null && EF.Functions.Like(p.Person.Name, $"%{searchQuery}%"))
.Select(p => p.Person);
// Get people from ChapterPeople by navigating through Volume -> Series
var peopleFromChapterPeople = queryable
.Where(sm => seriesIds.Contains(sm.SeriesId))
.SelectMany(sm => sm.Series.Volumes)
.SelectMany(v => v.Chapters)
.SelectMany(ch => ch.People)
.Where(cp => cp.Person.Name != null && EF.Functions.Like(cp.Person.Name, $"%{searchQuery}%"))
.Select(cp => cp.Person);
// Combine both queries and ensure distinct results
return peopleFromSeriesMetadata
.Union(peopleFromChapterPeople)
.Distinct()
.OrderBy(p => p.NormalizedName);
}

View file

@ -471,22 +471,22 @@ public static class SeriesFilter
{
case FilterComparison.Equal:
case FilterComparison.Contains:
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.Id)));
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId)));
case FilterComparison.NotEqual:
case FilterComparison.NotContains:
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.Id)));
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.PersonId)));
case FilterComparison.MustContains:
// Deconstruct and do a Union of a bunch of where statements since this doesn't translate
var queries = new List<IQueryable<Series>>()
{
queryable
};
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.Id == gId))));
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == gId))));
return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty:
// Check if there are no people with specific roles (e.g., Writer, Penciller, etc.)
return queryable.Where(s => !s.Metadata.People.Any(p => p.Role == role));
return queryable.Where(s => s.Metadata.People.All(p => p.Role != role));
case FilterComparison.GreaterThan:
case FilterComparison.GreaterThanEqual:
case FilterComparison.LessThan:
@ -513,17 +513,17 @@ public static class SeriesFilter
{
case FilterComparison.Equal:
case FilterComparison.Contains:
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.Id)));
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId)));
case FilterComparison.NotEqual:
case FilterComparison.NotContains:
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.Id)));
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.PersonId)));
case FilterComparison.MustContains:
// Deconstruct and do a Union of a bunch of where statements since this doesn't translate
var queries = new List<IQueryable<Series>>()
{
queryable
};
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.Id == gId))));
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == gId))));
return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty:

View file

@ -56,7 +56,8 @@ public static class IncludesExtensions
if (includes.HasFlag(ChapterIncludes.People))
{
queryable = queryable
.Include(c => c.People);
.Include(c => c.People)
.ThenInclude(cp => cp.Person);
}
if (includes.HasFlag(ChapterIncludes.Genres))
@ -161,17 +162,16 @@ public static class IncludesExtensions
if (includeFlags.HasFlag(SeriesIncludes.Metadata))
{
query = query.Include(s => s.Metadata)
.ThenInclude(m => m.CollectionTags.OrderBy(g => g.NormalizedTitle))
query = query
.Include(s => s.Metadata)
.ThenInclude(m => m.Genres.OrderBy(g => g.NormalizedTitle))
.Include(s => s.Metadata)
.ThenInclude(m => m.People)
.ThenInclude(smp => smp.Person)
.Include(s => s.Metadata)
.ThenInclude(m => m.Tags.OrderBy(g => g.NormalizedTitle));
}
return query.AsSplitQuery();
}

View file

@ -25,6 +25,19 @@ public static class RestrictByAgeExtensions
return q;
}
public static IQueryable<Chapter> RestrictAgainstAgeRestriction(this IQueryable<Chapter> queryable, AgeRestriction restriction)
{
if (restriction.AgeRating == AgeRating.NotApplicable) return queryable;
var q = queryable.Where(chapter => chapter.Volume.Series.Metadata.AgeRating <= restriction.AgeRating);
if (!restriction.IncludeUnknowns)
{
return q.Where(s => s.Volume.Series.Metadata.AgeRating != AgeRating.Unknown);
}
return q;
}
[Obsolete]
public static IQueryable<CollectionTag> RestrictAgainstAgeRestriction(this IQueryable<CollectionTag> queryable, AgeRestriction restriction)
{
@ -88,12 +101,12 @@ public static class RestrictByAgeExtensions
if (restriction.IncludeUnknowns)
{
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
sm.AgeRating <= restriction.AgeRating));
return queryable.Where(c => c.SeriesMetadataPeople.All(sm =>
sm.SeriesMetadata.AgeRating <= restriction.AgeRating));
}
return queryable.Where(c => c.SeriesMetadatas.All(sm =>
sm.AgeRating <= restriction.AgeRating && sm.AgeRating > AgeRating.Unknown));
return queryable.Where(c => c.SeriesMetadataPeople.All(sm =>
sm.SeriesMetadata.AgeRating <= restriction.AgeRating && sm.SeriesMetadata.AgeRating > AgeRating.Unknown));
}
public static IQueryable<ReadingList> RestrictAgainstAgeRestriction(this IQueryable<ReadingList> queryable, AgeRestriction restriction)

View file

@ -28,6 +28,12 @@ public static class SeriesExtensions
firstVolume = volumes[1];
}
// If the first volume is 0, then use Volume 1
if (firstVolume.MinNumber.Is(0f) && volumes.Count > 1)
{
firstVolume = volumes[1];
}
var chapters = firstVolume.Chapters
.OrderBy(c => c.SortOrder)
.ToList();