.NET 7 + Spring Cleaning (#1677)
* Updated to net7.0 * Updated GA to .net 7 * Updated System.IO.Abstractions to use New factory. * Converted Regex into SourceGenerator in Parser. * Updated more regex to source generators. * Enabled Nullability and more regex changes throughout codebase. * Parser is 100% GeneratedRegexified * Lots of nullability code * Enabled nullability for all repositories. * Fixed another unit test * Refactored some code around and took care of some todos. * Updating code for nullability and cleaning up methods that aren't used anymore. Refctored all uses of Parser.Normalize() to use new extension * More nullability exercises. 500 warnings to go. * Fixed a bug where custom file uploads for entities wouldn't save in webP. * Nullability is done for all DTOs * Fixed all unit tests and nullability for the project. Only OPDS is left which will be done with an upcoming OPDS enhancement. * Use localization in book service after validating * Code smells * Switched to preview build of swashbuckle for .net7 support * Fixed up merge issues * Disable emulate comic book when on single page reader * Fixed a regression where double page renderer wouldn't layout the images correctly * Updated to swashbuckle which support .net 7 * Fixed a bad GA action * Some code cleanup * More code smells * Took care of most of nullable issues * Fixed a broken test due to having more than one test run in parallel * I'm really not sure why the unit tests are failing or are so extremely slow on .net 7 * Updated all dependencies * Fixed up build and removed hardcoded framework from build scripts. (this merge removes Regex Source generators). Unit tests are completely busted. * Unit tests and code cleanup. Needs shakeout now. * Adjusted Series model since a few fields are not-nullable. Removed dead imports on the project. * Refactored to use Builder pattern for all unit tests. * Switched nullability down to warnings. It wasn't possible to switch due to constraint issues in DB Migration.
This commit is contained in:
parent
76fe3fd64a
commit
5d1dd7b3f0
283 changed files with 4221 additions and 4593 deletions
|
@ -23,29 +23,29 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
ChangeTracker.StateChanged += OnEntityStateChanged;
|
||||
}
|
||||
|
||||
public DbSet<Library> Library { get; set; }
|
||||
public DbSet<Series> Series { get; set; }
|
||||
public DbSet<Chapter> Chapter { get; set; }
|
||||
public DbSet<Volume> Volume { get; set; }
|
||||
public DbSet<AppUser> AppUser { get; set; }
|
||||
public DbSet<MangaFile> MangaFile { get; set; }
|
||||
public DbSet<AppUserProgress> AppUserProgresses { get; set; }
|
||||
public DbSet<AppUserRating> AppUserRating { get; set; }
|
||||
public DbSet<ServerSetting> ServerSetting { get; set; }
|
||||
public DbSet<AppUserPreferences> AppUserPreferences { get; set; }
|
||||
public DbSet<SeriesMetadata> SeriesMetadata { get; set; }
|
||||
public DbSet<CollectionTag> CollectionTag { get; set; }
|
||||
public DbSet<AppUserBookmark> AppUserBookmark { get; set; }
|
||||
public DbSet<ReadingList> ReadingList { get; set; }
|
||||
public DbSet<ReadingListItem> ReadingListItem { get; set; }
|
||||
public DbSet<Person> Person { get; set; }
|
||||
public DbSet<Genre> Genre { get; set; }
|
||||
public DbSet<Tag> Tag { get; set; }
|
||||
public DbSet<SiteTheme> SiteTheme { get; set; }
|
||||
public DbSet<SeriesRelation> SeriesRelation { get; set; }
|
||||
public DbSet<FolderPath> FolderPath { get; set; }
|
||||
public DbSet<Device> Device { get; set; }
|
||||
public DbSet<ServerStatistics> ServerStatistics { get; set; }
|
||||
public DbSet<Library> Library { get; set; } = null!;
|
||||
public DbSet<Series> Series { get; set; } = null!;
|
||||
public DbSet<Chapter> Chapter { get; set; } = null!;
|
||||
public DbSet<Volume> Volume { get; set; } = null!;
|
||||
public DbSet<AppUser> AppUser { get; set; } = null!;
|
||||
public DbSet<MangaFile> MangaFile { get; set; } = null!;
|
||||
public DbSet<AppUserProgress> AppUserProgresses { get; set; } = null!;
|
||||
public DbSet<AppUserRating> AppUserRating { get; set; } = null!;
|
||||
public DbSet<ServerSetting> ServerSetting { get; set; } = null!;
|
||||
public DbSet<AppUserPreferences> AppUserPreferences { get; set; } = null!;
|
||||
public DbSet<SeriesMetadata> SeriesMetadata { get; set; } = null!;
|
||||
public DbSet<CollectionTag> CollectionTag { get; set; } = null!;
|
||||
public DbSet<AppUserBookmark> AppUserBookmark { get; set; } = null!;
|
||||
public DbSet<ReadingList> ReadingList { get; set; } = null!;
|
||||
public DbSet<ReadingListItem> ReadingListItem { get; set; } = null!;
|
||||
public DbSet<Person> Person { get; set; } = null!;
|
||||
public DbSet<Genre> Genre { get; set; } = null!;
|
||||
public DbSet<Tag> Tag { get; set; } = null!;
|
||||
public DbSet<SiteTheme> SiteTheme { get; set; } = null!;
|
||||
public DbSet<SeriesRelation> SeriesRelation { get; set; } = null!;
|
||||
public DbSet<FolderPath> FolderPath { get; set; } = null!;
|
||||
public DbSet<Device> Device { get; set; } = null!;
|
||||
public DbSet<ServerStatistics> ServerStatistics { get; set; } = null!;
|
||||
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
|
@ -110,7 +110,7 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
}
|
||||
|
||||
|
||||
private static void OnEntityTracked(object sender, EntityTrackedEventArgs e)
|
||||
private static void OnEntityTracked(object? sender, EntityTrackedEventArgs e)
|
||||
{
|
||||
if (e.FromQuery || e.Entry.State != EntityState.Added || e.Entry.Entity is not IEntityDate entity) return;
|
||||
|
||||
|
@ -120,7 +120,7 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
entity.LastModifiedUtc = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private static void OnEntityStateChanged(object sender, EntityStateChangedEventArgs e)
|
||||
private static void OnEntityStateChanged(object? sender, EntityStateChangedEventArgs e)
|
||||
{
|
||||
if (e.NewState != EntityState.Modified || e.Entry.Entity is not IEntityDate entity) return;
|
||||
entity.LastModified = DateTime.Now;
|
||||
|
@ -142,28 +142,28 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
|
|||
|
||||
public override int SaveChanges()
|
||||
{
|
||||
this.OnSaveChanges();
|
||||
OnSaveChanges();
|
||||
|
||||
return base.SaveChanges();
|
||||
}
|
||||
|
||||
public override int SaveChanges(bool acceptAllChangesOnSuccess)
|
||||
{
|
||||
this.OnSaveChanges();
|
||||
OnSaveChanges();
|
||||
|
||||
return base.SaveChanges(acceptAllChangesOnSuccess);
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
this.OnSaveChanges();
|
||||
OnSaveChanges();
|
||||
|
||||
return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
|
||||
}
|
||||
|
||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
this.OnSaveChanges();
|
||||
OnSaveChanges();
|
||||
|
||||
return base.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using API.Data.Metadata;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using API.Extensions;
|
||||
using API.Parser;
|
||||
using API.Services.Tasks;
|
||||
using Kavita.Common;
|
||||
|
||||
namespace API.Data;
|
||||
|
||||
|
@ -16,6 +15,17 @@ namespace API.Data;
|
|||
/// </summary>
|
||||
public static class DbFactory
|
||||
{
|
||||
public static Library Library(string name, LibraryType type)
|
||||
{
|
||||
return new Library()
|
||||
{
|
||||
Name = name,
|
||||
Type = type,
|
||||
Series = new List<Series>(),
|
||||
Folders = new List<FolderPath>(),
|
||||
AppUsers = new List<AppUser>()
|
||||
};
|
||||
}
|
||||
public static Series Series(string name)
|
||||
{
|
||||
return new Series
|
||||
|
@ -23,8 +33,8 @@ public static class DbFactory
|
|||
Name = name,
|
||||
OriginalName = name,
|
||||
LocalizedName = name,
|
||||
NormalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedLocalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedName = name.ToNormalized(),
|
||||
NormalizedLocalizedName = name.ToNormalized(),
|
||||
SortName = name,
|
||||
Volumes = new List<Volume>(),
|
||||
Metadata = SeriesMetadata(new List<CollectionTag>())
|
||||
|
@ -42,8 +52,8 @@ public static class DbFactory
|
|||
Name = name,
|
||||
OriginalName = name,
|
||||
LocalizedName = localizedName,
|
||||
NormalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedLocalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(localizedName),
|
||||
NormalizedName = name.ToNormalized(),
|
||||
NormalizedLocalizedName = localizedName.ToNormalized(),
|
||||
SortName = name,
|
||||
Volumes = new List<Volume>(),
|
||||
Metadata = SeriesMetadata(new List<CollectionTag>())
|
||||
|
@ -85,28 +95,31 @@ public static class DbFactory
|
|||
};
|
||||
}
|
||||
|
||||
public static CollectionTag CollectionTag(int id, string title, string summary, bool promoted)
|
||||
public static CollectionTag CollectionTag(int id, string title, string? summary = null, bool promoted = false)
|
||||
{
|
||||
title = title.Trim();
|
||||
return new CollectionTag()
|
||||
{
|
||||
Id = id,
|
||||
NormalizedTitle = Services.Tasks.Scanner.Parser.Parser.Normalize(title?.Trim()),
|
||||
Title = title?.Trim(),
|
||||
NormalizedTitle = title.ToNormalized(),
|
||||
Title = title,
|
||||
Summary = summary?.Trim(),
|
||||
Promoted = promoted,
|
||||
SeriesMetadatas = new List<SeriesMetadata>()
|
||||
};
|
||||
}
|
||||
|
||||
public static ReadingList ReadingList(string title, string summary, bool promoted)
|
||||
public static ReadingList ReadingList(string title, string? summary = null, bool promoted = false, AgeRating rating = AgeRating.Unknown)
|
||||
{
|
||||
title = title.Trim();
|
||||
return new ReadingList()
|
||||
{
|
||||
NormalizedTitle = Services.Tasks.Scanner.Parser.Parser.Normalize(title?.Trim()),
|
||||
Title = title?.Trim(),
|
||||
NormalizedTitle = title.ToNormalized(),
|
||||
Title = title,
|
||||
Summary = summary?.Trim(),
|
||||
Promoted = promoted,
|
||||
Items = new List<ReadingListItem>()
|
||||
Items = new List<ReadingListItem>(),
|
||||
AgeRating = rating
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -126,7 +139,7 @@ public static class DbFactory
|
|||
return new Genre()
|
||||
{
|
||||
Title = name.Trim().SentenceCase(),
|
||||
NormalizedTitle = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedTitle = name.ToNormalized()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -135,7 +148,7 @@ public static class DbFactory
|
|||
return new Tag()
|
||||
{
|
||||
Title = name.Trim().SentenceCase(),
|
||||
NormalizedTitle = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedTitle = name.ToNormalized()
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -144,7 +157,7 @@ public static class DbFactory
|
|||
return new Person()
|
||||
{
|
||||
Name = name.Trim(),
|
||||
NormalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(name),
|
||||
NormalizedName = name.ToNormalized(),
|
||||
Role = role
|
||||
};
|
||||
}
|
||||
|
@ -169,4 +182,17 @@ public static class DbFactory
|
|||
};
|
||||
}
|
||||
|
||||
public static AppUser AppUser(string username, string email, SiteTheme defaultTheme)
|
||||
{
|
||||
return new AppUser()
|
||||
{
|
||||
UserName = username,
|
||||
Email = email,
|
||||
ApiKey = HashUtil.ApiKey(),
|
||||
UserPreferences = new AppUserPreferences
|
||||
{
|
||||
Theme = defaultTheme
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ public class ComicInfo
|
|||
.SingleOrDefault(t => t.ToDescription().ToUpperInvariant().Equals(value.ToUpperInvariant()), Entities.Enums.AgeRating.Unknown);
|
||||
}
|
||||
|
||||
public static void CleanComicInfo(ComicInfo info)
|
||||
public static void CleanComicInfo(ComicInfo? info)
|
||||
{
|
||||
if (info == null) return;
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
using API.Services;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using SQLitePCL;
|
||||
|
||||
namespace API.Data;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Enums;
|
||||
using CsvHelper;
|
||||
|
@ -15,9 +14,9 @@ namespace API.Data;
|
|||
|
||||
internal sealed class SeriesRelationMigrationOutput
|
||||
{
|
||||
public string SeriesName { get; set; }
|
||||
public required string SeriesName { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public string TargetSeriesName { get; set; }
|
||||
public required string TargetSeriesName { get; set; }
|
||||
public int TargetId { get; set; }
|
||||
public RelationKind Relationship { get; set; }
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using CsvHelper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
|
|
@ -1,13 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Metadata;
|
||||
using CsvHelper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Data;
|
||||
|
|
|
@ -9,13 +9,13 @@ public class RecentlyAddedSeries
|
|||
public LibraryType LibraryType { get; init; }
|
||||
public DateTime Created { get; init; }
|
||||
public int SeriesId { get; init; }
|
||||
public string SeriesName { get; init; }
|
||||
public string? SeriesName { get; init; }
|
||||
public MangaFormat Format { get; init; }
|
||||
public int ChapterId { get; init; }
|
||||
public int VolumeId { get; init; }
|
||||
public string ChapterNumber { get; init; }
|
||||
public string ChapterRange { get; init; }
|
||||
public string ChapterTitle { get; init; }
|
||||
public string? ChapterNumber { get; init; }
|
||||
public string? ChapterRange { get; init; }
|
||||
public string? ChapterTitle { get; init; }
|
||||
public bool IsSpecial { get; init; }
|
||||
public int VolumeNumber { get; init; }
|
||||
public AgeRating AgeRating { get; init; }
|
||||
|
|
|
@ -15,13 +15,13 @@ public interface IAppUserProgressRepository
|
|||
void Update(AppUserProgress userProgress);
|
||||
Task<int> CleanupAbandonedChapters();
|
||||
Task<bool> UserHasProgress(LibraryType libraryType, int userId);
|
||||
Task<AppUserProgress> GetUserProgressAsync(int chapterId, int userId);
|
||||
Task<AppUserProgress?> GetUserProgressAsync(int chapterId, int userId);
|
||||
Task<bool> HasAnyProgressOnSeriesAsync(int seriesId, int userId);
|
||||
/// <summary>
|
||||
/// This is built exclusively for <see cref="MigrateUserProgressLibraryId"/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<AppUserProgress> GetAnyProgress();
|
||||
Task<AppUserProgress?> GetAnyProgress();
|
||||
Task<IEnumerable<AppUserProgress>> GetUserProgressForSeriesAsync(int seriesId, int userId);
|
||||
Task<IEnumerable<AppUserProgress>> GetAllProgress();
|
||||
Task<ProgressDto> GetUserProgressDtoAsync(int chapterId, int userId);
|
||||
|
@ -97,7 +97,7 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
.AnyAsync(aup => aup.PagesRead > 0 && aup.AppUserId == userId && aup.SeriesId == seriesId);
|
||||
}
|
||||
|
||||
public async Task<AppUserProgress> GetAnyProgress()
|
||||
public async Task<AppUserProgress?> GetAnyProgress()
|
||||
{
|
||||
return await _context.AppUserProgresses.FirstOrDefaultAsync();
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
{
|
||||
return await _context.AppUserProgresses.ToListAsync();
|
||||
}
|
||||
|
||||
|
||||
public async Task<ProgressDto> GetUserProgressDtoAsync(int chapterId, int userId)
|
||||
{
|
||||
return await _context.AppUserProgresses
|
||||
|
@ -128,7 +128,7 @@ public class AppUserProgressRepository : IAppUserProgressRepository
|
|||
.FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserProgress> GetUserProgressAsync(int chapterId, int userId)
|
||||
public async Task<AppUserProgress?> GetUserProgressAsync(int chapterId, int userId)
|
||||
{
|
||||
return await _context.AppUserProgresses
|
||||
.Where(p => p.ChapterId == chapterId && p.AppUserId == userId)
|
||||
|
|
|
@ -25,15 +25,15 @@ public interface IChapterRepository
|
|||
{
|
||||
void Update(Chapter chapter);
|
||||
Task<IEnumerable<Chapter>> GetChaptersByIdsAsync(IList<int> chapterIds, ChapterIncludes includes = ChapterIncludes.None);
|
||||
Task<IChapterInfoDto> GetChapterInfoDtoAsync(int chapterId);
|
||||
Task<IChapterInfoDto?> GetChapterInfoDtoAsync(int chapterId);
|
||||
Task<int> GetChapterTotalPagesAsync(int chapterId);
|
||||
Task<Chapter> GetChapterAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<ChapterDto> GetChapterDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<ChapterMetadataDto> GetChapterMetadataDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<Chapter?> GetChapterAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<ChapterDto?> GetChapterDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<ChapterMetadataDto?> GetChapterMetadataDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files);
|
||||
Task<IList<MangaFile>> GetFilesForChapterAsync(int chapterId);
|
||||
Task<IList<Chapter>> GetChaptersAsync(int volumeId);
|
||||
Task<IList<MangaFile>> GetFilesForChaptersAsync(IReadOnlyList<int> chapterIds);
|
||||
Task<string> GetChapterCoverImageAsync(int chapterId);
|
||||
Task<string?> GetChapterCoverImageAsync(int chapterId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<IList<Chapter>> GetAllChaptersWithNonWebPCovers();
|
||||
Task<IEnumerable<string>> GetCoverImagesForLockedChaptersAsync();
|
||||
|
@ -68,7 +68,7 @@ public class ChapterRepository : IChapterRepository
|
|||
/// Populates a partial IChapterInfoDto
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IChapterInfoDto> GetChapterInfoDtoAsync(int chapterId)
|
||||
public async Task<IChapterInfoDto?> GetChapterInfoDtoAsync(int chapterId)
|
||||
{
|
||||
var chapterInfo = await _context.Chapter
|
||||
.Where(c => c.Id == chapterId)
|
||||
|
@ -124,7 +124,7 @@ public class ChapterRepository : IChapterRepository
|
|||
.Select(c => c.Pages)
|
||||
.FirstOrDefaultAsync();
|
||||
}
|
||||
public async Task<ChapterDto> GetChapterDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
public async Task<ChapterDto?> GetChapterDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
{
|
||||
var chapter = await _context.Chapter
|
||||
.Includes(includes)
|
||||
|
@ -136,7 +136,7 @@ public class ChapterRepository : IChapterRepository
|
|||
return chapter;
|
||||
}
|
||||
|
||||
public async Task<ChapterMetadataDto> GetChapterMetadataDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
public async Task<ChapterMetadataDto?> GetChapterMetadataDtoAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
{
|
||||
var chapter = await _context.Chapter
|
||||
.Includes(includes)
|
||||
|
@ -167,7 +167,7 @@ public class ChapterRepository : IChapterRepository
|
|||
/// <param name="chapterId"></param>
|
||||
/// <param name="includes"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Chapter> GetChapterAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
public async Task<Chapter?> GetChapterAsync(int chapterId, ChapterIncludes includes = ChapterIncludes.Files)
|
||||
{
|
||||
return await _context.Chapter
|
||||
.Includes(includes)
|
||||
|
@ -191,23 +191,20 @@ public class ChapterRepository : IChapterRepository
|
|||
/// </summary>
|
||||
/// <param name="chapterId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> GetChapterCoverImageAsync(int chapterId)
|
||||
public async Task<string?> GetChapterCoverImageAsync(int chapterId)
|
||||
{
|
||||
|
||||
return await _context.Chapter
|
||||
.Where(c => c.Id == chapterId)
|
||||
.Select(c => c.CoverImage)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return await _context.Chapter
|
||||
return (await _context.Chapter
|
||||
.Select(c => c.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<IList<Chapter>> GetAllChaptersWithNonWebPCovers()
|
||||
|
@ -223,12 +220,11 @@ public class ChapterRepository : IChapterRepository
|
|||
/// <returns></returns>
|
||||
public async Task<IEnumerable<string>> GetCoverImagesForLockedChaptersAsync()
|
||||
{
|
||||
return await _context.Chapter
|
||||
return (await _context.Chapter
|
||||
.Where(c => c.CoverImageLocked)
|
||||
.Select(c => c.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -25,10 +25,9 @@ public interface ICollectionTagRepository
|
|||
void Remove(CollectionTag tag);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync();
|
||||
Task<IEnumerable<CollectionTagDto>> SearchTagDtosAsync(string searchQuery, int userId);
|
||||
Task<string> GetCoverImageAsync(int collectionTagId);
|
||||
Task<string?> GetCoverImageAsync(int collectionTagId);
|
||||
Task<IEnumerable<CollectionTagDto>> GetAllPromotedTagDtosAsync(int userId);
|
||||
Task<CollectionTag> GetTagAsync(int tagId);
|
||||
Task<CollectionTag> GetFullTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.SeriesMetadata);
|
||||
Task<CollectionTag?> GetTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
void Update(CollectionTag tag);
|
||||
Task<int> RemoveTagsWithoutSeries();
|
||||
Task<IEnumerable<CollectionTag>> GetAllTagsAsync(CollectionTagIncludes includes = CollectionTagIncludes.None);
|
||||
|
@ -84,29 +83,27 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<string> GetCoverImageAsync(int collectionTagId)
|
||||
public async Task<string?> GetCoverImageAsync(int collectionTagId)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
.Where(c => c.Id == collectionTagId)
|
||||
.Select(c => c.CoverImage)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
return (await _context.CollectionTag
|
||||
.Select(t => t.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<bool> TagExists(string title)
|
||||
{
|
||||
var normalized = Services.Tasks.Scanner.Parser.Parser.Normalize(title);
|
||||
var normalized = title.ToNormalized();
|
||||
return await _context.CollectionTag
|
||||
.AnyAsync(x => x.NormalizedTitle.Equals(normalized));
|
||||
.AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CollectionTagDto>> GetAllTagDtosAsync()
|
||||
|
@ -131,14 +128,8 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<CollectionTag> GetTagAsync(int tagId)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
.Where(c => c.Id == tagId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<CollectionTag> GetFullTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.SeriesMetadata)
|
||||
public async Task<CollectionTag?> GetTagAsync(int tagId, CollectionTagIncludes includes = CollectionTagIncludes.None)
|
||||
{
|
||||
return await _context.CollectionTag
|
||||
.Where(c => c.Id == tagId)
|
||||
|
@ -164,8 +155,8 @@ public class CollectionTagRepository : ICollectionTagRepository
|
|||
{
|
||||
var userRating = await GetUserAgeRestriction(userId);
|
||||
return await _context.CollectionTag
|
||||
.Where(s => EF.Functions.Like(s.Title, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.NormalizedTitle, $"%{searchQuery}%"))
|
||||
.Where(s => EF.Functions.Like(s.Title!, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.NormalizedTitle!, $"%{searchQuery}%"))
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.OrderBy(s => s.NormalizedTitle)
|
||||
.AsNoTracking()
|
||||
|
|
|
@ -13,7 +13,7 @@ public interface IDeviceRepository
|
|||
{
|
||||
void Update(Device device);
|
||||
Task<IEnumerable<DeviceDto>> GetDevicesForUserAsync(int userId);
|
||||
Task<Device> GetDeviceById(int deviceId);
|
||||
Task<Device?> GetDeviceById(int deviceId);
|
||||
}
|
||||
|
||||
public class DeviceRepository : IDeviceRepository
|
||||
|
@ -41,7 +41,7 @@ public class DeviceRepository : IDeviceRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Device> GetDeviceById(int deviceId)
|
||||
public async Task<Device?> GetDeviceById(int deviceId)
|
||||
{
|
||||
return await _context.Device
|
||||
.Where(d => d.Id == deviceId)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data.Misc;
|
||||
using API.DTOs.Metadata;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
|
@ -15,7 +14,7 @@ public interface IGenreRepository
|
|||
{
|
||||
void Attach(Genre genre);
|
||||
void Remove(Genre genre);
|
||||
Task<Genre> FindByNameAsync(string genreName);
|
||||
Task<Genre?> FindByNameAsync(string genreName);
|
||||
Task<IList<Genre>> GetAllGenresAsync();
|
||||
Task<IList<GenreTagDto>> GetAllGenreDtosAsync(int userId);
|
||||
Task RemoveAllGenreNoLongerAssociated(bool removeExternal = false);
|
||||
|
@ -44,11 +43,11 @@ public class GenreRepository : IGenreRepository
|
|||
_context.Genre.Remove(genre);
|
||||
}
|
||||
|
||||
public async Task<Genre> FindByNameAsync(string genreName)
|
||||
public async Task<Genre?> FindByNameAsync(string genreName)
|
||||
{
|
||||
var normalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize(genreName);
|
||||
var normalizedName = genreName.ToNormalized();
|
||||
return await _context.Genre
|
||||
.FirstOrDefaultAsync(g => g.NormalizedTitle.Equals(normalizedName));
|
||||
.FirstOrDefaultAsync(g => g.NormalizedTitle != null && g.NormalizedTitle.Equals(normalizedName));
|
||||
}
|
||||
|
||||
public async Task RemoveAllGenreNoLongerAssociated(bool removeExternal = false)
|
||||
|
|
|
@ -31,13 +31,12 @@ public interface ILibraryRepository
|
|||
{
|
||||
void Add(Library library);
|
||||
void Update(Library library);
|
||||
void Delete(Library library);
|
||||
void Delete(Library? library);
|
||||
Task<IEnumerable<LibraryDto>> GetLibraryDtosAsync();
|
||||
Task<bool> LibraryExists(string libraryName);
|
||||
Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None);
|
||||
Task<Library?> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None);
|
||||
Task<IEnumerable<LibraryDto>> GetLibraryDtosForUsernameAsync(string userName);
|
||||
Task<IEnumerable<Library>> GetLibrariesAsync(LibraryIncludes includes = LibraryIncludes.None);
|
||||
Task<bool> DeleteLibrary(int libraryId);
|
||||
Task<IEnumerable<Library>> GetLibrariesForUserIdAsync(int userId);
|
||||
IEnumerable<int> GetLibraryIdsForUserIdAsync(int userId, QueryContext queryContext = QueryContext.None);
|
||||
Task<LibraryType> GetLibraryTypeAsync(int libraryId);
|
||||
|
@ -49,7 +48,7 @@ public interface ILibraryRepository
|
|||
Task<IList<LanguageDto>> GetAllLanguagesForLibrariesAsync();
|
||||
IEnumerable<PublicationStatusDto> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds);
|
||||
Task<bool> DoAnySeriesFoldersMatch(IEnumerable<string> folders);
|
||||
Task<string> GetLibraryCoverImageAsync(int libraryId);
|
||||
Task<string?> GetLibraryCoverImageAsync(int libraryId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<IDictionary<int, LibraryType>> GetLibraryTypesForIdsAsync(IEnumerable<int> libraryIds);
|
||||
}
|
||||
|
@ -75,8 +74,9 @@ public class LibraryRepository : ILibraryRepository
|
|||
_context.Entry(library).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Delete(Library library)
|
||||
public void Delete(Library? library)
|
||||
{
|
||||
if (library == null) return;
|
||||
_context.Library.Remove(library);
|
||||
}
|
||||
|
||||
|
@ -107,14 +107,6 @@ public class LibraryRepository : ILibraryRepository
|
|||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteLibrary(int libraryId)
|
||||
{
|
||||
var library = await GetLibraryForIdAsync(libraryId, LibraryIncludes.Folders | LibraryIncludes.Series);
|
||||
_context.Library.Remove(library);
|
||||
|
||||
return await _context.SaveChangesAsync() > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This does not track
|
||||
/// </summary>
|
||||
|
@ -164,7 +156,7 @@ public class LibraryRepository : ILibraryRepository
|
|||
public IEnumerable<JumpKeyDto> GetJumpBarAsync(int libraryId)
|
||||
{
|
||||
var seriesSortCharacters = _context.Series.Where(s => s.LibraryId == libraryId)
|
||||
.Select(s => s.SortName.ToUpper())
|
||||
.Select(s => s.SortName!.ToUpper())
|
||||
.OrderBy(s => s)
|
||||
.AsEnumerable()
|
||||
.Select(s => s[0]);
|
||||
|
@ -207,7 +199,7 @@ public class LibraryRepository : ILibraryRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None)
|
||||
public async Task<Library?> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None)
|
||||
{
|
||||
|
||||
var query = _context.Library
|
||||
|
@ -237,54 +229,11 @@ public class LibraryRepository : ILibraryRepository
|
|||
return query.AsSplitQuery();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This returns a Library with all it's Series -> Volumes -> Chapters. This is expensive. Should only be called when needed.
|
||||
/// </summary>
|
||||
/// <param name="libraryId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Library> GetFullLibraryForIdAsync(int libraryId)
|
||||
{
|
||||
return await _context.Library
|
||||
.Where(x => x.Id == libraryId)
|
||||
.Include(f => f.Folders)
|
||||
.Include(l => l.Series)
|
||||
.ThenInclude(s => s.Metadata)
|
||||
.Include(l => l.Series)
|
||||
.ThenInclude(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a heavy call, pulls all entities for a Library, except this version only grabs for one series id
|
||||
/// </summary>
|
||||
/// <param name="libraryId"></param>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Library> GetFullLibraryForIdAsync(int libraryId, int seriesId)
|
||||
{
|
||||
|
||||
return await _context.Library
|
||||
.Where(x => x.Id == libraryId)
|
||||
.Include(f => f.Folders)
|
||||
.Include(l => l.Series.Where(s => s.Id == seriesId))
|
||||
.ThenInclude(s => s.Metadata)
|
||||
.Include(l => l.Series.Where(s => s.Id == seriesId))
|
||||
.ThenInclude(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> LibraryExists(string libraryName)
|
||||
{
|
||||
return await _context.Library
|
||||
.AsNoTracking()
|
||||
.AnyAsync(x => x.Name.Equals(libraryName));
|
||||
.AnyAsync(x => x.Name != null && x.Name.Equals(libraryName));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LibraryDto>> GetLibrariesForUserAsync(AppUser user)
|
||||
|
@ -381,7 +330,7 @@ public class LibraryRepository : ILibraryRepository
|
|||
return await _context.Series.AnyAsync(s => normalized.Contains(s.FolderPath));
|
||||
}
|
||||
|
||||
public Task<string> GetLibraryCoverImageAsync(int libraryId)
|
||||
public Task<string?> GetLibraryCoverImageAsync(int libraryId)
|
||||
{
|
||||
return _context.Library
|
||||
.Where(l => l.Id == libraryId)
|
||||
|
@ -392,11 +341,10 @@ public class LibraryRepository : ILibraryRepository
|
|||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return await _context.ReadingList
|
||||
return (await _context.ReadingList
|
||||
.Select(t => t.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<IDictionary<int, LibraryType>> GetLibraryTypesForIdsAsync(IEnumerable<int> libraryIds)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
@ -17,12 +16,10 @@ public interface IMangaFileRepository
|
|||
public class MangaFileRepository : IMangaFileRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public MangaFileRepository(DataContext context, IMapper mapper)
|
||||
public MangaFileRepository(DataContext context)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Update(MangaFile file)
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.ReadingLists;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Services;
|
||||
using AutoMapper;
|
||||
|
@ -16,11 +15,11 @@ namespace API.Data.Repositories;
|
|||
public interface IReadingListRepository
|
||||
{
|
||||
Task<PagedList<ReadingListDto>> GetReadingListDtosForUserAsync(int userId, bool includePromoted, UserParams userParams);
|
||||
Task<ReadingList> GetReadingListByIdAsync(int readingListId);
|
||||
Task<ReadingList?> GetReadingListByIdAsync(int readingListId);
|
||||
Task<IEnumerable<ReadingListItemDto>> GetReadingListItemDtosByIdAsync(int readingListId, int userId);
|
||||
Task<ReadingListDto> GetReadingListDtoByIdAsync(int readingListId, int userId);
|
||||
Task<ReadingListDto?> GetReadingListDtoByIdAsync(int readingListId, int userId);
|
||||
Task<IEnumerable<ReadingListItemDto>> AddReadingProgressModifiers(int userId, IList<ReadingListItemDto> items);
|
||||
Task<ReadingListDto> GetReadingListDtoByTitleAsync(int userId, string title);
|
||||
Task<ReadingListDto?> GetReadingListDtoByTitleAsync(int userId, string title);
|
||||
Task<IEnumerable<ReadingListItem>> GetReadingListItemsByIdAsync(int readingListId);
|
||||
Task<IEnumerable<ReadingListDto>> GetReadingListDtosForSeriesAndUserAsync(int userId, int seriesId,
|
||||
bool includePromoted);
|
||||
|
@ -29,7 +28,7 @@ public interface IReadingListRepository
|
|||
void BulkRemove(IEnumerable<ReadingListItem> items);
|
||||
void Update(ReadingList list);
|
||||
Task<int> Count();
|
||||
Task<string> GetCoverImageAsync(int readingListId);
|
||||
Task<string?> GetCoverImageAsync(int readingListId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<bool> ReadingListExists(string name);
|
||||
Task<List<ReadingList>> GetAllReadingListsAsync();
|
||||
|
@ -61,29 +60,27 @@ public class ReadingListRepository : IReadingListRepository
|
|||
return await _context.ReadingList.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<string> GetCoverImageAsync(int readingListId)
|
||||
public async Task<string?> GetCoverImageAsync(int readingListId)
|
||||
{
|
||||
return await _context.ReadingList
|
||||
.Where(c => c.Id == readingListId)
|
||||
.Select(c => c.CoverImage)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return await _context.ReadingList
|
||||
return (await _context.ReadingList
|
||||
.Select(t => t.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<bool> ReadingListExists(string name)
|
||||
{
|
||||
var normalized = Services.Tasks.Scanner.Parser.Parser.Normalize(name);
|
||||
var normalized = name.ToNormalized();
|
||||
return await _context.ReadingList
|
||||
.AnyAsync(x => x.NormalizedTitle.Equals(normalized));
|
||||
.AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized));
|
||||
}
|
||||
|
||||
public async Task<List<ReadingList>> GetAllReadingListsAsync()
|
||||
|
@ -132,7 +129,7 @@ public class ReadingListRepository : IReadingListRepository
|
|||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<ReadingList> GetReadingListByIdAsync(int readingListId)
|
||||
public async Task<ReadingList?> GetReadingListByIdAsync(int readingListId)
|
||||
{
|
||||
return await _context.ReadingList
|
||||
.Where(r => r.Id == readingListId)
|
||||
|
@ -241,7 +238,7 @@ public class ReadingListRepository : IReadingListRepository
|
|||
return items;
|
||||
}
|
||||
|
||||
public async Task<ReadingListDto> GetReadingListDtoByIdAsync(int readingListId, int userId)
|
||||
public async Task<ReadingListDto?> GetReadingListDtoByIdAsync(int readingListId, int userId)
|
||||
{
|
||||
return await _context.ReadingList
|
||||
.Where(r => r.Id == readingListId && (r.AppUserId == userId || r.Promoted))
|
||||
|
@ -261,14 +258,14 @@ public class ReadingListRepository : IReadingListRepository
|
|||
{
|
||||
var progress = userProgress.Where(p => p.ChapterId == item.ChapterId).ToList();
|
||||
if (progress.Count == 0) continue;
|
||||
item.PagesRead = progress.Sum(p => p.PagesRead);
|
||||
item.PagesRead = progress.Sum(p => p.PagesRead);
|
||||
item.LastReadingProgressUtc = progress.Max(p => p.LastModifiedUtc);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public async Task<ReadingListDto> GetReadingListDtoByTitleAsync(int userId, string title)
|
||||
public async Task<ReadingListDto?> GetReadingListDtoByTitleAsync(int userId, string title)
|
||||
{
|
||||
return await _context.ReadingList
|
||||
.Where(r => r.Title.Equals(title) && r.AppUserId == userId)
|
||||
|
|
|
@ -78,7 +78,7 @@ public interface ISeriesRepository
|
|||
Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, IList<int> libraryIds, string searchQuery);
|
||||
Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId, int userId);
|
||||
Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata);
|
||||
Task<Series?> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata);
|
||||
Task<IList<Series>> GetSeriesByIdsAsync(IList<int> seriesIds);
|
||||
Task<int[]> GetChapterIdsForSeriesAsync(IList<int> seriesIds);
|
||||
Task<IDictionary<int, IList<int>>> GetChapterIdWithSeriesIdForSeriesAsync(int[] seriesIds);
|
||||
|
@ -89,20 +89,19 @@ public interface ISeriesRepository
|
|||
/// <param name="series"></param>
|
||||
/// <returns></returns>
|
||||
Task AddSeriesModifiers(int userId, List<SeriesDto> series);
|
||||
Task<string> GetSeriesCoverImageAsync(int seriesId);
|
||||
Task<string?> GetSeriesCoverImageAsync(int seriesId);
|
||||
Task<PagedList<SeriesDto>> GetOnDeck(int userId, int libraryId, UserParams userParams, FilterDto filter);
|
||||
Task<PagedList<SeriesDto>> GetRecentlyAdded(int libraryId, int userId, UserParams userParams, FilterDto filter);
|
||||
Task<SeriesMetadataDto> GetSeriesMetadata(int seriesId);
|
||||
Task<SeriesMetadataDto?> GetSeriesMetadata(int seriesId);
|
||||
Task<PagedList<SeriesDto>> GetSeriesDtoForCollectionAsync(int collectionId, int userId, UserParams userParams);
|
||||
Task<IList<MangaFile>> GetFilesForSeries(int seriesId);
|
||||
Task<IEnumerable<SeriesDto>> GetSeriesDtoForIdsAsync(IEnumerable<int> seriesIds, int userId);
|
||||
Task<IList<string>> GetAllCoverImagesAsync();
|
||||
Task<IEnumerable<string>> GetLockedCoverImagesAsync();
|
||||
Task<PagedList<Series>> GetFullSeriesForLibraryIdAsync(int libraryId, UserParams userParams);
|
||||
Task<Series> GetFullSeriesForSeriesIdAsync(int seriesId);
|
||||
Task<Series?> GetFullSeriesForSeriesIdAsync(int seriesId);
|
||||
Task<Chunk> GetChunkInfo(int libraryId = 0);
|
||||
Task<IList<SeriesMetadata>> GetSeriesMetadataForIdsAsync(IEnumerable<int> seriesIds);
|
||||
|
||||
Task<IEnumerable<GroupedSeriesDto>> GetRecentlyUpdatedSeries(int userId, int pageSize = 30);
|
||||
Task<RelatedSeriesDto> GetRelatedSeries(int userId, int seriesId);
|
||||
Task<IEnumerable<SeriesDto>> GetSeriesForRelationKind(int userId, int seriesId, RelationKind kind);
|
||||
|
@ -111,20 +110,19 @@ public interface ISeriesRepository
|
|||
Task<PagedList<SeriesDto>> GetHighlyRated(int userId, int libraryId, UserParams userParams);
|
||||
Task<PagedList<SeriesDto>> GetMoreIn(int userId, int libraryId, int genreId, UserParams userParams);
|
||||
Task<PagedList<SeriesDto>> GetRediscover(int userId, int libraryId, UserParams userParams);
|
||||
Task<SeriesDto> GetSeriesForMangaFile(int mangaFileId, int userId);
|
||||
Task<SeriesDto> GetSeriesForChapter(int chapterId, int userId);
|
||||
Task<SeriesDto?> GetSeriesForMangaFile(int mangaFileId, int userId);
|
||||
Task<SeriesDto?> GetSeriesForChapter(int chapterId, int userId);
|
||||
Task<PagedList<SeriesDto>> GetWantToReadForUserAsync(int userId, UserParams userParams, FilterDto filter);
|
||||
Task<bool> IsSeriesInWantToRead(int userId, int seriesId);
|
||||
Task<Series> GetSeriesByFolderPath(string folder, SeriesIncludes includes = SeriesIncludes.None);
|
||||
|
||||
Task<Series?> GetSeriesByFolderPath(string folder, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<IEnumerable<Series>> GetAllSeriesByNameAsync(IList<string> normalizedNames,
|
||||
int userId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<IEnumerable<SeriesDto>> GetAllSeriesDtosByNameAsync(IEnumerable<string> normalizedNames,
|
||||
int userId, SeriesIncludes includes = SeriesIncludes.None);
|
||||
Task<Series> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true);
|
||||
Task<Series?> GetFullSeriesByAnyName(string seriesName, string localizedName, int libraryId, MangaFormat format, bool withFullIncludes = true);
|
||||
Task<IList<Series>> RemoveSeriesNotInList(IList<ParsedSeries> seenSeries, int libraryId);
|
||||
Task<IDictionary<string, IList<SeriesModified>>> GetFolderPathMap(int libraryId);
|
||||
Task<AgeRating> GetMaxAgeRatingFromSeriesAsync(IEnumerable<int> seriesIds);
|
||||
Task<AgeRating?> GetMaxAgeRatingFromSeriesAsync(IEnumerable<int> seriesIds);
|
||||
/// <summary>
|
||||
/// This is only used for <see cref="MigrateUserProgressLibraryId"/>
|
||||
/// </summary>
|
||||
|
@ -138,6 +136,12 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
|
||||
// [GeneratedRegex(@"\d{4}", RegexOptions.Compiled, 50000)]
|
||||
// private static partial Regex YearRegex();
|
||||
private readonly Regex _yearRegex = new Regex(@"\d{4}", RegexOptions.Compiled, Services.Tasks.Scanner.Parser.Parser.RegexTimeout);
|
||||
|
||||
public SeriesRepository(DataContext context, IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
|
@ -202,6 +206,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <returns></returns>
|
||||
public async Task<PagedList<Series>> GetFullSeriesForLibraryIdAsync(int libraryId, UserParams userParams)
|
||||
{
|
||||
#nullable disable
|
||||
var query = _context.Series
|
||||
.Where(s => s.LibraryId == libraryId)
|
||||
|
||||
|
@ -229,11 +234,12 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ThenInclude(v => v.Chapters)
|
||||
.ThenInclude(c => c.Tags)
|
||||
|
||||
.Include(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
.Include(s => s.Volumes)!
|
||||
.ThenInclude(v => v.Chapters)!
|
||||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.OrderBy(s => s.SortName.ToLower());
|
||||
#nullable enable
|
||||
|
||||
return await PagedList<Series>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
@ -243,8 +249,9 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// </summary>
|
||||
/// <param name="seriesId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Series> GetFullSeriesForSeriesIdAsync(int seriesId)
|
||||
public async Task<Series?> GetFullSeriesForSeriesIdAsync(int seriesId)
|
||||
{
|
||||
#nullable disable
|
||||
return await _context.Series
|
||||
.Where(s => s.Id == seriesId)
|
||||
.Include(s => s.Relations)
|
||||
|
@ -274,6 +281,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.SingleOrDefaultAsync();
|
||||
#nullable enable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -313,7 +321,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
const int maxRecords = 15;
|
||||
var result = new SearchResultGroupDto();
|
||||
var searchQueryNormalized = Services.Tasks.Scanner.Parser.Parser.Normalize(searchQuery);
|
||||
var searchQueryNormalized = searchQuery.ToNormalized();
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
var seriesIds = _context.Series
|
||||
|
@ -332,20 +340,20 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
|
||||
var justYear = Regex.Match(searchQuery, @"\d{4}", RegexOptions.None, Services.Tasks.Scanner.Parser.Parser.RegexTimeout).Value;
|
||||
var justYear = _yearRegex.Match(searchQuery).Value;
|
||||
var hasYearInQuery = !string.IsNullOrEmpty(justYear);
|
||||
var yearComparison = hasYearInQuery ? int.Parse(justYear) : 0;
|
||||
|
||||
result.Series = _context.Series
|
||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||
.Where(s => EF.Functions.Like(s.Name, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.OriginalName, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(s.NormalizedName, $"%{searchQueryNormalized}%")
|
||||
|| (hasYearInQuery && s.Metadata.ReleaseYear == yearComparison))
|
||||
.Where(s => (EF.Functions.Like(s.Name, $"%{searchQuery}%")
|
||||
|| (s.OriginalName != null && EF.Functions.Like(s.OriginalName, $"%{searchQuery}%"))
|
||||
|| (s.LocalizedName != null && EF.Functions.Like(s.LocalizedName, $"%{searchQuery}%"))
|
||||
|| (EF.Functions.Like(s.NormalizedName, $"%{searchQueryNormalized}%"))
|
||||
|| (hasYearInQuery && s.Metadata.ReleaseYear == yearComparison)))
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.Include(s => s.Library)
|
||||
.OrderBy(s => s.SortName.ToLower())
|
||||
.OrderBy(s => s.SortName!.ToLower())
|
||||
.AsNoTracking()
|
||||
.AsSplitQuery()
|
||||
.Take(maxRecords)
|
||||
|
@ -362,8 +370,8 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ToListAsync();
|
||||
|
||||
result.Collections = await _context.CollectionTag
|
||||
.Where(c => EF.Functions.Like(c.Title, $"%{searchQuery}%")
|
||||
|| EF.Functions.Like(c.NormalizedTitle, $"%{searchQueryNormalized}%"))
|
||||
.Where(c => (EF.Functions.Like(c.Title, $"%{searchQuery}%"))
|
||||
|| (EF.Functions.Like(c.NormalizedTitle, $"%{searchQueryNormalized}%")))
|
||||
.Where(c => c.Promoted || isAdmin)
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.OrderBy(s => s.NormalizedTitle)
|
||||
|
@ -376,7 +384,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
result.Persons = await _context.SeriesMetadata
|
||||
.Where(sm => seriesIds.Contains(sm.SeriesId))
|
||||
.SelectMany(sm => sm.People.Where(t => EF.Functions.Like(t.Name, $"%{searchQuery}%")))
|
||||
.SelectMany(sm => sm.People.Where(t => t.Name != null && EF.Functions.Like(t.Name, $"%{searchQuery}%")))
|
||||
.AsSplitQuery()
|
||||
.Take(maxRecords)
|
||||
.Distinct()
|
||||
|
@ -448,7 +456,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <param name="seriesId"></param>
|
||||
/// <param name="includes"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata)
|
||||
public async Task<Series?> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata)
|
||||
{
|
||||
return await _context.Series
|
||||
.Where(s => s.Id == seriesId)
|
||||
|
@ -581,12 +589,11 @@ public class SeriesRepository : ISeriesRepository
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<string> GetSeriesCoverImageAsync(int seriesId)
|
||||
public async Task<string?> GetSeriesCoverImageAsync(int seriesId)
|
||||
{
|
||||
return await _context.Series
|
||||
.Where(s => s.Id == seriesId)
|
||||
.Select(s => s.CoverImage)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
@ -711,7 +718,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
var cutoffProgressPoint = DateTime.Now - TimeSpan.FromDays(30);
|
||||
var cutoffLastAddedPoint = DateTime.Now - TimeSpan.FromDays(7);
|
||||
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Dashboard)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Dashboard)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
|
||||
|
@ -767,12 +774,12 @@ public class SeriesRepository : ISeriesRepository
|
|||
.WhereIf(hasAgeRating, s => filter.AgeRating.Contains(s.Metadata.AgeRating))
|
||||
.WhereIf(hasTagsFilter, s => s.Metadata.Tags.Any(t => filter.Tags.Contains(t.Id)))
|
||||
.WhereIf(hasLanguageFilter, s => filter.Languages.Contains(s.Metadata.Language))
|
||||
.WhereIf(hasReleaseYearMinFilter, s => s.Metadata.ReleaseYear >= filter.ReleaseYearRange.Min)
|
||||
.WhereIf(hasReleaseYearMaxFilter, s => s.Metadata.ReleaseYear <= filter.ReleaseYearRange.Max)
|
||||
.WhereIf(hasReleaseYearMinFilter, s => s.Metadata.ReleaseYear >= filter.ReleaseYearRange!.Min)
|
||||
.WhereIf(hasReleaseYearMaxFilter, s => s.Metadata.ReleaseYear <= filter.ReleaseYearRange!.Max)
|
||||
.WhereIf(hasPublicationFilter, s => filter.PublicationStatus.Contains(s.Metadata.PublicationStatus))
|
||||
.WhereIf(hasSeriesNameFilter, s => EF.Functions.Like(s.Name, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.OriginalName, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName, $"%{filter.SeriesNameQuery}%"))
|
||||
|| EF.Functions.Like(s.OriginalName!, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName!, $"%{filter.SeriesNameQuery}%"))
|
||||
|
||||
.WhereIf(onlyParentSeries,
|
||||
s => s.RelationOf.Count == 0 || s.RelationOf.All(p => p.RelationKind == RelationKind.Prequel))
|
||||
|
@ -841,12 +848,12 @@ public class SeriesRepository : ISeriesRepository
|
|||
.WhereIf(hasAgeRating, s => filter.AgeRating.Contains(s.Metadata.AgeRating))
|
||||
.WhereIf(hasTagsFilter, s => s.Metadata.Tags.Any(t => filter.Tags.Contains(t.Id)))
|
||||
.WhereIf(hasLanguageFilter, s => filter.Languages.Contains(s.Metadata.Language))
|
||||
.WhereIf(hasReleaseYearMinFilter, s => s.Metadata.ReleaseYear >= filter.ReleaseYearRange.Min)
|
||||
.WhereIf(hasReleaseYearMaxFilter, s => s.Metadata.ReleaseYear <= filter.ReleaseYearRange.Max)
|
||||
.WhereIf(hasReleaseYearMinFilter, s => s.Metadata.ReleaseYear >= filter.ReleaseYearRange!.Min)
|
||||
.WhereIf(hasReleaseYearMaxFilter, s => s.Metadata.ReleaseYear <= filter.ReleaseYearRange!.Max)
|
||||
.WhereIf(hasPublicationFilter, s => filter.PublicationStatus.Contains(s.Metadata.PublicationStatus))
|
||||
.WhereIf(hasSeriesNameFilter, s => EF.Functions.Like(s.Name, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.OriginalName, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName, $"%{filter.SeriesNameQuery}%"))
|
||||
|| EF.Functions.Like(s.OriginalName!, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName!, $"%{filter.SeriesNameQuery}%"))
|
||||
.Where(s => userLibraries.Contains(s.LibraryId)
|
||||
&& formats.Contains(s.Format))
|
||||
.AsNoTracking();
|
||||
|
@ -862,7 +869,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
query = filter.SortOptions.SortField switch
|
||||
{
|
||||
SortField.SortName => query.OrderBy(s => s.SortName.ToLower()),
|
||||
SortField.SortName => query.OrderBy(s => s.SortName!.ToLower()),
|
||||
SortField.CreatedDate => query.OrderBy(s => s.Created),
|
||||
SortField.LastModifiedDate => query.OrderBy(s => s.LastModified),
|
||||
SortField.LastChapterAdded => query.OrderBy(s => s.LastChapterAdded),
|
||||
|
@ -874,7 +881,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
{
|
||||
query = filter.SortOptions.SortField switch
|
||||
{
|
||||
SortField.SortName => query.OrderByDescending(s => s.SortName.ToLower()),
|
||||
SortField.SortName => query.OrderByDescending(s => s.SortName!.ToLower()),
|
||||
SortField.CreatedDate => query.OrderByDescending(s => s.Created),
|
||||
SortField.LastModifiedDate => query.OrderByDescending(s => s.LastModified),
|
||||
SortField.LastChapterAdded => query.OrderByDescending(s => s.LastChapterAdded),
|
||||
|
@ -886,7 +893,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
return query;
|
||||
}
|
||||
|
||||
public async Task<SeriesMetadataDto> GetSeriesMetadata(int seriesId)
|
||||
public async Task<SeriesMetadataDto?> GetSeriesMetadata(int seriesId)
|
||||
{
|
||||
var metadataDto = await _context.SeriesMetadata
|
||||
.Where(metadata => metadata.SeriesId == seriesId)
|
||||
|
@ -968,20 +975,18 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<IList<string>> GetAllCoverImagesAsync()
|
||||
{
|
||||
return await _context.Series
|
||||
return (await _context.Series
|
||||
.Select(s => s.CoverImage)
|
||||
.Where(t => !string.IsNullOrEmpty(t))
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetLockedCoverImagesAsync()
|
||||
{
|
||||
return await _context.Series
|
||||
return (await _context.Series
|
||||
.Where(s => s.CoverImageLocked && !string.IsNullOrEmpty(s.CoverImage))
|
||||
.Select(s => s.CoverImage)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
.ToListAsync())!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1061,31 +1066,35 @@ public class SeriesRepository : ISeriesRepository
|
|||
var items = (await GetRecentlyAddedChaptersQuery(userId));
|
||||
if (userRating.AgeRating != AgeRating.NotApplicable)
|
||||
{
|
||||
items = items.RestrictAgainstAgeRestriction(userRating);
|
||||
items = items.RestrictAgainstAgeRestriction(userRating);
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (seriesMap.Keys.Count == pageSize) break;
|
||||
if (seriesMap.Keys.Count == pageSize) break;
|
||||
|
||||
if (seriesMap.ContainsKey(item.SeriesName))
|
||||
{
|
||||
seriesMap[item.SeriesName].Count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
seriesMap[item.SeriesName] = new GroupedSeriesDto()
|
||||
{
|
||||
LibraryId = item.LibraryId,
|
||||
LibraryType = item.LibraryType,
|
||||
SeriesId = item.SeriesId,
|
||||
SeriesName = item.SeriesName,
|
||||
Created = item.Created,
|
||||
Id = index,
|
||||
Format = item.Format,
|
||||
Count = 1,
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
if (item.SeriesName == null) continue;
|
||||
|
||||
|
||||
if (seriesMap.TryGetValue(item.SeriesName, out var value))
|
||||
{
|
||||
value.Count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
seriesMap[item.SeriesName] = new GroupedSeriesDto()
|
||||
{
|
||||
LibraryId = item.LibraryId,
|
||||
LibraryType = item.LibraryType,
|
||||
SeriesId = item.SeriesId,
|
||||
SeriesName = item.SeriesName,
|
||||
Created = item.Created,
|
||||
Id = index,
|
||||
Format = item.Format,
|
||||
Count = 1,
|
||||
};
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return seriesMap.Values.AsEnumerable();
|
||||
|
@ -1093,7 +1102,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<IEnumerable<SeriesDto>> GetSeriesForRelationKind(int userId, int seriesId, RelationKind kind)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId);
|
||||
var libraryIds = GetLibraryIdsForUser(userId);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
var usersSeriesIds = _context.Series
|
||||
|
@ -1120,7 +1129,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<PagedList<SeriesDto>> GetMoreIn(int userId, int libraryId, int genreId, UserParams userParams)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Recommended)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Recommended)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
|
||||
|
@ -1148,7 +1157,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <returns></returns>
|
||||
public async Task<PagedList<SeriesDto>> GetRediscover(int userId, int libraryId, UserParams userParams)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Recommended)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Recommended)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
var distinctSeriesIdsWithProgress = _context.AppUserProgresses
|
||||
|
@ -1166,9 +1175,9 @@ public class SeriesRepository : ISeriesRepository
|
|||
return await PagedList<SeriesDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
public async Task<SeriesDto> GetSeriesForMangaFile(int mangaFileId, int userId)
|
||||
public async Task<SeriesDto?> GetSeriesForMangaFile(int mangaFileId, int userId)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Search);
|
||||
var libraryIds = GetLibraryIdsForUser(userId, 0, QueryContext.Search);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
return await _context.MangaFile
|
||||
|
@ -1183,9 +1192,9 @@ public class SeriesRepository : ISeriesRepository
|
|||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<SeriesDto> GetSeriesForChapter(int chapterId, int userId)
|
||||
public async Task<SeriesDto?> GetSeriesForChapter(int chapterId, int userId)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId);
|
||||
var libraryIds = GetLibraryIdsForUser(userId);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
return await _context.Chapter
|
||||
.Where(m => m.Id == chapterId)
|
||||
|
@ -1204,11 +1213,11 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// <param name="folder">This will be normalized in the query</param>
|
||||
/// <param name="includes">Additional relationships to include with the base query</param>
|
||||
/// <returns></returns>
|
||||
public async Task<Series> GetSeriesByFolderPath(string folder, SeriesIncludes includes = SeriesIncludes.None)
|
||||
public async Task<Series?> GetSeriesByFolderPath(string folder, SeriesIncludes includes = SeriesIncludes.None)
|
||||
{
|
||||
var normalized = Services.Tasks.Scanner.Parser.Parser.NormalizePath(folder);
|
||||
return await _context.Series
|
||||
.Where(s => s.FolderPath.Equals(normalized))
|
||||
.Where(s => s.FolderPath != null && s.FolderPath.Equals(normalized))
|
||||
.Includes(includes)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
@ -1220,7 +1229,8 @@ public class SeriesRepository : ISeriesRepository
|
|||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
|
||||
return await _context.Series
|
||||
.Where(s => normalizedNames.Contains(s.NormalizedName) || normalizedNames.Contains(s.NormalizedLocalizedName))
|
||||
.Where(s => normalizedNames.Contains(s.NormalizedName) ||
|
||||
normalizedNames.Contains(s.NormalizedLocalizedName))
|
||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||
.RestrictAgainstAgeRestriction(userRating)
|
||||
.Includes(includes)
|
||||
|
@ -1252,21 +1262,23 @@ 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 = Services.Tasks.Scanner.Parser.Parser.Normalize(seriesName);
|
||||
var normalizedLocalized = Services.Tasks.Scanner.Parser.Parser.Normalize(localizedName);
|
||||
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.Equals(normalizedSeries) && s.NormalizedLocalizedName != string.Empty)
|
||||
|| s.OriginalName.Equals(seriesName));
|
||||
|| (s.NormalizedLocalizedName == normalizedSeries)
|
||||
|| (s.OriginalName == seriesName));
|
||||
|
||||
if (!string.IsNullOrEmpty(normalizedLocalized))
|
||||
{
|
||||
// TODO: Apply WhereIf
|
||||
query = query.Where(s =>
|
||||
s.NormalizedName.Equals(normalizedLocalized) || s.NormalizedLocalizedName.Equals(normalizedLocalized));
|
||||
s.NormalizedName.Equals(normalizedLocalized)
|
||||
|| (s.NormalizedLocalizedName != null && s.NormalizedLocalizedName.Equals(normalizedLocalized)));
|
||||
}
|
||||
|
||||
if (!withFullIncludes)
|
||||
|
@ -1274,10 +1286,13 @@ public class SeriesRepository : ISeriesRepository
|
|||
return query.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
#nullable disable
|
||||
return query.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.People)
|
||||
|
||||
.Include(s => s.Metadata)
|
||||
.ThenInclude(m => m.Genres)
|
||||
|
||||
.Include(s => s.Library)
|
||||
.Include(s => s.Volumes)
|
||||
.ThenInclude(v => v.Chapters)
|
||||
|
@ -1300,6 +1315,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
.ThenInclude(c => c.Files)
|
||||
.AsSplitQuery()
|
||||
.SingleOrDefaultAsync();
|
||||
#nullable enable
|
||||
}
|
||||
|
||||
|
||||
|
@ -1356,7 +1372,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<PagedList<SeriesDto>> GetHighlyRated(int userId, int libraryId, UserParams userParams)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Recommended)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Recommended)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
var distinctSeriesIdsWithHighRating = _context.AppUserRating
|
||||
|
@ -1378,7 +1394,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<PagedList<SeriesDto>> GetQuickReads(int userId, int libraryId, UserParams userParams)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Recommended)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Recommended)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
var distinctSeriesIdsWithProgress = _context.AppUserProgresses
|
||||
|
@ -1405,7 +1421,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
public async Task<PagedList<SeriesDto>> GetQuickCatchupReads(int userId, int libraryId, UserParams userParams)
|
||||
{
|
||||
var libraryIds = _context.Library.GetUserLibraries(userId, QueryContext.Recommended)
|
||||
var libraryIds = GetLibraryIdsForUser(userId, libraryId, QueryContext.Recommended)
|
||||
.Where(id => libraryId == 0 || id == libraryId);
|
||||
var usersSeriesIds = GetSeriesIdsForLibraryIds(libraryIds);
|
||||
var distinctSeriesIdsWithProgress = _context.AppUserProgresses
|
||||
|
@ -1581,6 +1597,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
var map = new Dictionary<string, IList<SeriesModified>>();
|
||||
foreach (var series in info)
|
||||
{
|
||||
if (series.FolderPath == null) continue;
|
||||
if (!map.ContainsKey(series.FolderPath))
|
||||
{
|
||||
map.Add(series.FolderPath, new List<SeriesModified>()
|
||||
|
@ -1603,7 +1620,7 @@ public class SeriesRepository : ISeriesRepository
|
|||
/// </summary>
|
||||
/// <param name="seriesIds"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<AgeRating> GetMaxAgeRatingFromSeriesAsync(IEnumerable<int> seriesIds)
|
||||
public async Task<AgeRating?> GetMaxAgeRatingFromSeriesAsync(IEnumerable<int> seriesIds)
|
||||
{
|
||||
return await _context.Series
|
||||
.Where(s => seriesIds.Contains(s.Id))
|
||||
|
@ -1612,4 +1629,32 @@ public class SeriesRepository : ISeriesRepository
|
|||
.OrderBy(s => s)
|
||||
.LastOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all library ids for a user
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="libraryId">0 for no library filter</param>
|
||||
/// <param name="queryContext">Defaults to None - The context behind this query, so appropriate restrictions can be placed</param>
|
||||
/// <returns></returns>
|
||||
private IQueryable<int> GetLibraryIdsForUser(int userId, int libraryId = 0, QueryContext queryContext = QueryContext.None)
|
||||
{
|
||||
var user = _context.AppUser
|
||||
.AsSplitQuery()
|
||||
.AsNoTracking()
|
||||
.Where(u => u.Id == userId)
|
||||
.AsSingleQuery();
|
||||
|
||||
if (libraryId == 0)
|
||||
{
|
||||
return user.SelectMany(l => l.Libraries)
|
||||
.IsRestricted(queryContext)
|
||||
.Select(lib => lib.Id);
|
||||
}
|
||||
|
||||
return user.SelectMany(l => l.Libraries)
|
||||
.Where(lib => lib.Id == libraryId)
|
||||
.IsRestricted(queryContext)
|
||||
.Select(lib => lib.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ using API.DTOs.Settings;
|
|||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
@ -44,7 +43,7 @@ public class SettingsRepository : ISettingsRepository
|
|||
|
||||
public Task<ServerSetting> GetSettingAsync(ServerSettingKey key)
|
||||
{
|
||||
return _context.ServerSetting.SingleOrDefaultAsync(x => x.Key == key);
|
||||
return _context.ServerSetting.SingleOrDefaultAsync(x => x.Key == key)!;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ServerSetting>> GetSettingsAsync()
|
||||
|
|
|
@ -15,11 +15,11 @@ public interface ISiteThemeRepository
|
|||
void Remove(SiteTheme theme);
|
||||
void Update(SiteTheme siteTheme);
|
||||
Task<IEnumerable<SiteThemeDto>> GetThemeDtos();
|
||||
Task<SiteThemeDto> GetThemeDto(int themeId);
|
||||
Task<SiteThemeDto> GetThemeDtoByName(string themeName);
|
||||
Task<SiteThemeDto?> GetThemeDto(int themeId);
|
||||
Task<SiteThemeDto?> GetThemeDtoByName(string themeName);
|
||||
Task<SiteTheme> GetDefaultTheme();
|
||||
Task<IEnumerable<SiteTheme>> GetThemes();
|
||||
Task<SiteTheme> GetThemeById(int themeId);
|
||||
Task<SiteTheme?> GetThemeById(int themeId);
|
||||
}
|
||||
|
||||
public class SiteThemeRepository : ISiteThemeRepository
|
||||
|
@ -55,7 +55,7 @@ public class SiteThemeRepository : ISiteThemeRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<SiteThemeDto> GetThemeDtoByName(string themeName)
|
||||
public async Task<SiteThemeDto?> GetThemeDtoByName(string themeName)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
.Where(t => t.Name.Equals(themeName))
|
||||
|
@ -76,8 +76,8 @@ public class SiteThemeRepository : ISiteThemeRepository
|
|||
if (result == null)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
.Where(t => t.NormalizedName == "dark")
|
||||
.SingleOrDefaultAsync();
|
||||
.Where(t => t.NormalizedName == Seed.DefaultThemes[0].NormalizedName)
|
||||
.SingleAsync();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -89,14 +89,14 @@ public class SiteThemeRepository : ISiteThemeRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<SiteTheme> GetThemeById(int themeId)
|
||||
public async Task<SiteTheme?> GetThemeById(int themeId)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
.Where(t => t.Id == themeId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<SiteThemeDto> GetThemeDto(int themeId)
|
||||
public async Task<SiteThemeDto?> GetThemeDto(int themeId)
|
||||
{
|
||||
return await _context.SiteTheme
|
||||
.Where(t => t.Id == themeId)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Constants;
|
||||
|
@ -9,11 +8,11 @@ using API.DTOs.Account;
|
|||
using API.DTOs.Filtering;
|
||||
using API.DTOs.Reader;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
||||
|
@ -38,31 +37,31 @@ public interface IUserRepository
|
|||
void Update(AppUserPreferences preferences);
|
||||
void Update(AppUserBookmark bookmark);
|
||||
void Add(AppUserBookmark bookmark);
|
||||
public void Delete(AppUser user);
|
||||
public void Delete(AppUser? user);
|
||||
void Delete(AppUserBookmark bookmark);
|
||||
Task<IEnumerable<MemberDto>> GetEmailConfirmedMemberDtosAsync();
|
||||
Task<IEnumerable<MemberDto>> GetPendingMemberDtosAsync();
|
||||
Task<IEnumerable<AppUser>> GetAdminUsersAsync();
|
||||
Task<bool> IsUserAdminAsync(AppUser user);
|
||||
Task<AppUserRating> GetUserRatingAsync(int seriesId, int userId);
|
||||
Task<AppUserPreferences> GetPreferencesAsync(string username);
|
||||
Task<bool> IsUserAdminAsync(AppUser? user);
|
||||
Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId);
|
||||
Task<AppUserPreferences?> GetPreferencesAsync(string username);
|
||||
Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForSeries(int userId, int seriesId);
|
||||
Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForVolume(int userId, int volumeId);
|
||||
Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForChapter(int userId, int chapterId);
|
||||
Task<IEnumerable<BookmarkDto>> GetAllBookmarkDtos(int userId, FilterDto filter);
|
||||
Task<IEnumerable<AppUserBookmark>> GetAllBookmarksAsync();
|
||||
Task<AppUserBookmark> GetBookmarkForPage(int page, int chapterId, int userId);
|
||||
Task<AppUserBookmark> GetBookmarkAsync(int bookmarkId);
|
||||
Task<AppUserBookmark?> GetBookmarkForPage(int page, int chapterId, int userId);
|
||||
Task<AppUserBookmark?> GetBookmarkAsync(int bookmarkId);
|
||||
Task<int> GetUserIdByApiKeyAsync(string apiKey);
|
||||
Task<AppUser> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None);
|
||||
Task<AppUser> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None);
|
||||
Task<AppUser?> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None);
|
||||
Task<AppUser?> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None);
|
||||
Task<int> GetUserIdByUsernameAsync(string username);
|
||||
Task<IList<AppUserBookmark>> GetAllBookmarksByIds(IList<int> bookmarkIds);
|
||||
Task<AppUser> GetUserByEmailAsync(string email);
|
||||
Task<AppUser?> GetUserByEmailAsync(string email);
|
||||
Task<IEnumerable<AppUserPreferences>> GetAllPreferencesByThemeAsync(int themeId);
|
||||
Task<bool> HasAccessToLibrary(int libraryId, int userId);
|
||||
Task<IEnumerable<AppUser>> GetAllUsersAsync(AppUserIncludes includeFlags = AppUserIncludes.None);
|
||||
Task<AppUser> GetUserByConfirmationToken(string token);
|
||||
Task<AppUser?> GetUserByConfirmationToken(string token);
|
||||
}
|
||||
|
||||
public class UserRepository : IUserRepository
|
||||
|
@ -98,8 +97,9 @@ public class UserRepository : IUserRepository
|
|||
_context.AppUserBookmark.Add(bookmark);
|
||||
}
|
||||
|
||||
public void Delete(AppUser user)
|
||||
public void Delete(AppUser? user)
|
||||
{
|
||||
if (user == null) return;
|
||||
_context.AppUser.Remove(user);
|
||||
}
|
||||
|
||||
|
@ -114,15 +114,12 @@ public class UserRepository : IUserRepository
|
|||
/// <param name="username"></param>
|
||||
/// <param name="includeFlags">Includes() you want. Pass multiple with flag1 | flag2 </param>
|
||||
/// <returns></returns>
|
||||
public async Task<AppUser> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None)
|
||||
public async Task<AppUser?> GetUserByUsernameAsync(string username, AppUserIncludes includeFlags = AppUserIncludes.None)
|
||||
{
|
||||
var query = _context.Users
|
||||
.Where(x => x.UserName == username);
|
||||
|
||||
// TODO: Move to QueryExtensions
|
||||
query = AddIncludesToQuery(query, includeFlags);
|
||||
|
||||
return await query.SingleOrDefaultAsync();
|
||||
return await _context.Users
|
||||
.Where(x => x.UserName == username)
|
||||
.Includes(includeFlags)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -131,14 +128,12 @@ public class UserRepository : IUserRepository
|
|||
/// <param name="userId"></param>
|
||||
/// <param name="includeFlags">Includes() you want. Pass multiple with flag1 | flag2 </param>
|
||||
/// <returns></returns>
|
||||
public async Task<AppUser> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None)
|
||||
public async Task<AppUser?> GetUserByIdAsync(int userId, AppUserIncludes includeFlags = AppUserIncludes.None)
|
||||
{
|
||||
var query = _context.Users
|
||||
.Where(x => x.Id == userId);
|
||||
|
||||
query = AddIncludesToQuery(query, includeFlags);
|
||||
|
||||
return await query.SingleOrDefaultAsync();
|
||||
return await _context.Users
|
||||
.Where(x => x.Id == userId)
|
||||
.Includes(includeFlags)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUserBookmark>> GetAllBookmarksAsync()
|
||||
|
@ -146,65 +141,20 @@ public class UserRepository : IUserRepository
|
|||
return await _context.AppUserBookmark.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserBookmark> GetBookmarkForPage(int page, int chapterId, int userId)
|
||||
public async Task<AppUserBookmark?> GetBookmarkForPage(int page, int chapterId, int userId)
|
||||
{
|
||||
return await _context.AppUserBookmark
|
||||
.Where(b => b.Page == page && b.ChapterId == chapterId && b.AppUserId == userId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserBookmark> GetBookmarkAsync(int bookmarkId)
|
||||
public async Task<AppUserBookmark?> GetBookmarkAsync(int bookmarkId)
|
||||
{
|
||||
return await _context.AppUserBookmark
|
||||
.Where(b => b.Id == bookmarkId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
private static IQueryable<AppUser> AddIncludesToQuery(IQueryable<AppUser> query, AppUserIncludes includeFlags)
|
||||
{
|
||||
if (includeFlags.HasFlag(AppUserIncludes.Bookmarks))
|
||||
{
|
||||
query = query.Include(u => u.Bookmarks);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.Progress))
|
||||
{
|
||||
query = query.Include(u => u.Progresses);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.ReadingLists))
|
||||
{
|
||||
query = query.Include(u => u.ReadingLists);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.ReadingListsWithItems))
|
||||
{
|
||||
query = query.Include(u => u.ReadingLists).ThenInclude(r => r.Items);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.Ratings))
|
||||
{
|
||||
query = query.Include(u => u.Ratings);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.UserPreferences))
|
||||
{
|
||||
query = query.Include(u => u.UserPreferences);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.WantToRead))
|
||||
{
|
||||
query = query.Include(u => u.WantToRead);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.Devices))
|
||||
{
|
||||
query = query.Include(u => u.Devices);
|
||||
}
|
||||
|
||||
return query.AsSplitQuery();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This fetches the Id for a user. Use whenever you just need an ID.
|
||||
|
@ -233,10 +183,10 @@ public class UserRepository : IUserRepository
|
|||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUser> GetUserByEmailAsync(string email)
|
||||
public async Task<AppUser?> GetUserByEmailAsync(string email)
|
||||
{
|
||||
var lowerEmail = email.ToLower();
|
||||
return await _context.AppUser.SingleOrDefaultAsync(u => u.Email.ToLower().Equals(lowerEmail));
|
||||
return await _context.AppUser.SingleOrDefaultAsync(u => u.Email != null && u.Email.ToLower().Equals(lowerEmail));
|
||||
}
|
||||
|
||||
|
||||
|
@ -259,13 +209,15 @@ public class UserRepository : IUserRepository
|
|||
|
||||
public async Task<IEnumerable<AppUser>> GetAllUsersAsync(AppUserIncludes includeFlags = AppUserIncludes.None)
|
||||
{
|
||||
var query = AddIncludesToQuery(_context.Users.AsQueryable(), includeFlags);
|
||||
return await query.ToListAsync();
|
||||
return await _context.AppUser
|
||||
.Includes(includeFlags)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUser> GetUserByConfirmationToken(string token)
|
||||
public async Task<AppUser?> GetUserByConfirmationToken(string token)
|
||||
{
|
||||
return await _context.AppUser.SingleOrDefaultAsync(u => u.ConfirmationToken.Equals(token));
|
||||
return await _context.AppUser
|
||||
.SingleOrDefaultAsync(u => u.ConfirmationToken != null && u.ConfirmationToken.Equals(token));
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUser>> GetAdminUsersAsync()
|
||||
|
@ -273,19 +225,20 @@ public class UserRepository : IUserRepository
|
|||
return await _userManager.GetUsersInRoleAsync(PolicyConstants.AdminRole);
|
||||
}
|
||||
|
||||
public async Task<bool> IsUserAdminAsync(AppUser user)
|
||||
public async Task<bool> IsUserAdminAsync(AppUser? user)
|
||||
{
|
||||
if (user == null) return false;
|
||||
return await _userManager.IsInRoleAsync(user, PolicyConstants.AdminRole);
|
||||
}
|
||||
|
||||
public async Task<AppUserRating> GetUserRatingAsync(int seriesId, int userId)
|
||||
public async Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId)
|
||||
{
|
||||
return await _context.AppUserRating
|
||||
.Where(r => r.SeriesId == seriesId && r.AppUserId == userId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserPreferences> GetPreferencesAsync(string username)
|
||||
public async Task<AppUserPreferences?> GetPreferencesAsync(string username)
|
||||
{
|
||||
return await _context.AppUserPreferences
|
||||
.Include(p => p.AppUser)
|
||||
|
@ -342,16 +295,16 @@ public class UserRepository : IUserRepository
|
|||
.ProjectTo<BookmarkDto>(_mapper.ConfigurationProvider)
|
||||
.ToListAsync();
|
||||
|
||||
var seriesNameQueryNormalized = Services.Tasks.Scanner.Parser.Parser.Normalize(filter.SeriesNameQuery);
|
||||
var seriesNameQueryNormalized = filter.SeriesNameQuery.ToNormalized();
|
||||
var filterSeriesQuery = query.Join(_context.Series, b => b.SeriesId, s => s.Id, (bookmark, series) => new
|
||||
{
|
||||
bookmark,
|
||||
series
|
||||
})
|
||||
.Where(o => EF.Functions.Like(o.series.Name, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(o.series.OriginalName, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(o.series.LocalizedName, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(o.series.NormalizedName, $"%{seriesNameQueryNormalized}%")
|
||||
.Where(o => (EF.Functions.Like(o.series.Name, $"%{filter.SeriesNameQuery}%"))
|
||||
|| (o.series.OriginalName != null && EF.Functions.Like(o.series.OriginalName, $"%{filter.SeriesNameQuery}%"))
|
||||
|| (o.series.LocalizedName != null && EF.Functions.Like(o.series.LocalizedName, $"%{filter.SeriesNameQuery}%"))
|
||||
|| (EF.Functions.Like(o.series.NormalizedName, $"%{seriesNameQueryNormalized}%"))
|
||||
);
|
||||
|
||||
query = filterSeriesQuery.Select(o => o.bookmark);
|
||||
|
@ -370,7 +323,7 @@ public class UserRepository : IUserRepository
|
|||
public async Task<int> GetUserIdByApiKeyAsync(string apiKey)
|
||||
{
|
||||
return await _context.AppUser
|
||||
.Where(u => u.ApiKey.Equals(apiKey))
|
||||
.Where(u => u.ApiKey != null && u.ApiKey.Equals(apiKey))
|
||||
.Select(u => u.Id)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
@ -391,7 +344,7 @@ public class UserRepository : IUserRepository
|
|||
Email = u.Email,
|
||||
Created = u.Created,
|
||||
LastActive = u.LastActive,
|
||||
Roles = u.UserRoles.Select(r => r.Role.Name).ToList(),
|
||||
Roles = u.UserRoles.Select(r => r.Role.Name).ToList()!,
|
||||
AgeRestriction = new AgeRestrictionDto()
|
||||
{
|
||||
AgeRating = u.AgeRestriction,
|
||||
|
@ -429,7 +382,7 @@ public class UserRepository : IUserRepository
|
|||
Email = u.Email,
|
||||
Created = u.Created,
|
||||
LastActive = u.LastActive,
|
||||
Roles = u.UserRoles.Select(r => r.Role.Name).ToList(),
|
||||
Roles = u.UserRoles.Select(r => r.Role.Name).ToList()!,
|
||||
AgeRestriction = new AgeRestrictionDto()
|
||||
{
|
||||
AgeRating = u.AgeRestriction,
|
||||
|
|
|
@ -16,14 +16,14 @@ public interface IVolumeRepository
|
|||
void Update(Volume volume);
|
||||
void Remove(Volume volume);
|
||||
Task<IList<MangaFile>> GetFilesForVolume(int volumeId);
|
||||
Task<string> GetVolumeCoverImageAsync(int volumeId);
|
||||
Task<string?> GetVolumeCoverImageAsync(int volumeId);
|
||||
Task<IList<int>> GetChapterIdsByVolumeIds(IReadOnlyList<int> volumeIds);
|
||||
Task<IEnumerable<VolumeDto>> GetVolumesDtoAsync(int seriesId, int userId);
|
||||
Task<Volume> GetVolumeAsync(int volumeId);
|
||||
Task<Volume?> GetVolumeAsync(int volumeId);
|
||||
Task<VolumeDto> GetVolumeDtoAsync(int volumeId, int userId);
|
||||
Task<IEnumerable<Volume>> GetVolumesForSeriesAsync(IList<int> seriesIds, bool includeChapters = false);
|
||||
Task<IEnumerable<Volume>> GetVolumes(int seriesId);
|
||||
Task<Volume> GetVolumeByIdAsync(int volumeId);
|
||||
Task<Volume?> GetVolumeByIdAsync(int volumeId);
|
||||
}
|
||||
public class VolumeRepository : IVolumeRepository
|
||||
{
|
||||
|
@ -72,12 +72,11 @@ public class VolumeRepository : IVolumeRepository
|
|||
/// </summary>
|
||||
/// <param name="volumeId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string> GetVolumeCoverImageAsync(int volumeId)
|
||||
public async Task<string?> GetVolumeCoverImageAsync(int volumeId)
|
||||
{
|
||||
return await _context.Volume
|
||||
.Where(v => v.Id == volumeId)
|
||||
.Select(v => v.CoverImage)
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
|
@ -155,7 +154,7 @@ public class VolumeRepository : IVolumeRepository
|
|||
/// </summary>
|
||||
/// <param name="volumeId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<Volume> GetVolumeAsync(int volumeId)
|
||||
public async Task<Volume?> GetVolumeAsync(int volumeId)
|
||||
{
|
||||
return await _context.Volume
|
||||
.Include(vol => vol.Chapters)
|
||||
|
@ -191,7 +190,7 @@ public class VolumeRepository : IVolumeRepository
|
|||
return volumes;
|
||||
}
|
||||
|
||||
public async Task<Volume> GetVolumeByIdAsync(int volumeId)
|
||||
public async Task<Volume?> GetVolumeByIdAsync(int volumeId)
|
||||
{
|
||||
return await _context.Volume.SingleOrDefaultAsync(x => x.Id == volumeId);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ using API.Constants;
|
|||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Entities.Enums.Theme;
|
||||
using API.Extensions;
|
||||
using API.Services;
|
||||
using Kavita.Common;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
|
@ -29,7 +30,7 @@ public static class Seed
|
|||
new()
|
||||
{
|
||||
Name = "Dark",
|
||||
NormalizedName = Services.Tasks.Scanner.Parser.Parser.Normalize("Dark"),
|
||||
NormalizedName = "Dark".ToNormalized(),
|
||||
Provider = ThemeProvider.System,
|
||||
FileName = "dark.scss",
|
||||
IsDefault = true,
|
||||
|
@ -42,13 +43,13 @@ public static class Seed
|
|||
.GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(f => f.FieldType == typeof(string))
|
||||
.ToDictionary(f => f.Name,
|
||||
f => (string) f.GetValue(null)).Values
|
||||
f => (string) f.GetValue(null)!).Values
|
||||
.Select(policyName => new AppRole() {Name = policyName})
|
||||
.ToList();
|
||||
|
||||
foreach (var role in roles)
|
||||
{
|
||||
var exists = await roleManager.RoleExistsAsync(role.Name);
|
||||
var exists = await roleManager.RoleExistsAsync(role.Name!);
|
||||
if (!exists)
|
||||
{
|
||||
await roleManager.CreateAsync(role);
|
||||
|
|
|
@ -60,7 +60,7 @@ public class UnitOfWork : IUnitOfWork
|
|||
public IGenreRepository GenreRepository => new GenreRepository(_context, _mapper);
|
||||
public ITagRepository TagRepository => new TagRepository(_context, _mapper);
|
||||
public ISiteThemeRepository SiteThemeRepository => new SiteThemeRepository(_context, _mapper);
|
||||
public IMangaFileRepository MangaFileRepository => new MangaFileRepository(_context, _mapper);
|
||||
public IMangaFileRepository MangaFileRepository => new MangaFileRepository(_context);
|
||||
public IDeviceRepository DeviceRepository => new DeviceRepository(_context, _mapper);
|
||||
|
||||
/// <summary>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue