OPDS Performance Enhancements (#4332)

Co-authored-by: Amelia <77553571+Fesaa@users.noreply.github.com>
This commit is contained in:
Joe Milazzo
2026-01-08 08:25:05 -07:00
committed by GitHub
parent 1480d20132
commit a7859e1a86
94 changed files with 4515 additions and 3124 deletions
+23 -89
View File
@@ -52,8 +52,7 @@ public interface IChapterRepository
Task<IList<string>> GetAllCoverImagesAsync();
Task<IList<Chapter>> GetAllChaptersWithCoversInDifferentEncoding(EncodeFormat format);
Task<IEnumerable<string>> GetCoverImagesForLockedChaptersAsync();
Task AddChapterModifiers(int userId, ChapterDto chapter);
IEnumerable<Chapter> GetChaptersForSeries(int seriesId);
IQueryable<Chapter> GetChaptersForSeries(int seriesId);
Task<IList<Chapter>> GetAllChaptersForSeries(int seriesId);
Task<int> GetAverageUserRating(int chapterId, int userId);
Task<IList<UserReviewDto>> GetExternalChapterReviewDtos(int chapterId);
@@ -63,6 +62,7 @@ public interface IChapterRepository
Task<ChapterDto?> GetCurrentlyReadingChapterAsync(int seriesId, int userId);
Task<ChapterDto?> GetFirstChapterForSeriesAsync(int seriesId, int userId);
Task<ChapterDto?> GetFirstChapterForVolumeAsync(int volumeId, int userId);
Task<IList<ChapterDto>> GetChapterDtosAsync(IEnumerable<int> chapterIds, int userId);
}
public class ChapterRepository : IChapterRepository
{
@@ -163,15 +163,10 @@ public class ChapterRepository : IChapterRepository
{
var chapter = await _context.Chapter
.Includes(ChapterIncludes.Files | ChapterIncludes.People)
.ProjectTo<ChapterDto>(_mapper.ConfigurationProvider)
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.AsSplitQuery()
.FirstOrDefaultAsync(c => c.Id == chapterId);
if (userId > 0 && chapter != null)
{
await AddChapterModifiers(userId, chapter);
}
return chapter;
}
@@ -180,15 +175,10 @@ public class ChapterRepository : IChapterRepository
var chapters = await _context.Chapter
.Where(c => chapterIds.Contains(c.Id))
.Includes(ChapterIncludes.Files | ChapterIncludes.People)
.ProjectTo<ChapterDto>(_mapper.ConfigurationProvider)
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.AsSplitQuery()
.ToListAsync() ;
foreach (var chapter in chapters)
{
await AddChapterModifiers(userId, chapter);
}
return chapters;
}
@@ -252,19 +242,12 @@ public class ChapterRepository : IChapterRepository
/// <returns></returns>
public async Task<IList<ChapterDto>> GetChapterDtosAsync(int volumeId, int userId)
{
var chapts = await _context.Chapter
return await _context.Chapter
.Where(c => c.VolumeId == volumeId)
.Includes(ChapterIncludes.Files | ChapterIncludes.People)
.OrderBy(c => c.SortOrder)
.ProjectTo<ChapterDto>(_mapper.ConfigurationProvider)
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.ToListAsync();
foreach (var chapter in chapts)
{
await AddChapterModifiers(userId, chapter);
}
return chapts;
}
/// <summary>
@@ -322,41 +305,17 @@ public class ChapterRepository : IChapterRepository
.ToListAsync();
}
public async Task AddChapterModifiers(int userId, ChapterDto? chapter)
{
if (chapter == null) return;
var progress = await _context.AppUserProgresses.Where(x =>
x.AppUserId == userId && x.ChapterId == chapter.Id)
.AsNoTracking()
.FirstOrDefaultAsync();
if (progress != null)
{
chapter.PagesRead = progress.PagesRead ;
chapter.LastReadingProgressUtc = progress.LastModifiedUtc;
chapter.LastReadingProgress = progress.LastModified;
chapter.TotalReads = progress.TotalReads;
}
else
{
chapter.PagesRead = 0;
chapter.LastReadingProgressUtc = DateTime.MinValue;
chapter.LastReadingProgress = DateTime.MinValue;
}
}
/// <summary>
/// Includes Volumes
/// </summary>
/// <param name="seriesId"></param>
/// <returns></returns>
public IEnumerable<Chapter> GetChaptersForSeries(int seriesId)
public IQueryable<Chapter> GetChaptersForSeries(int seriesId)
{
return _context.Chapter
.Where(c => c.Volume.SeriesId == seriesId)
.OrderBy(c => c.SortOrder)
.Include(c => c.Volume)
.AsEnumerable();
.Include(c => c.Volume);
}
public async Task<IList<Chapter>> GetAllChaptersForSeries(int seriesId)
@@ -453,62 +412,37 @@ public class ChapterRepository : IChapterRepository
public async Task<ChapterDto?> GetFirstChapterForSeriesAsync(int seriesId, int userId)
{
// Get the chapter entity with proper ordering
var firstChapter = await _context.Chapter
return await _context.Chapter
.Include(c => c.Volume)
.Include(c => c.Files)
.Where(c => c.Volume.SeriesId == seriesId)
.OrderBy(c =>
// Priority 1: Regular volumes (not loose leaf, not special)
c.Volume.Number == Parser.LooseLeafVolumeNumber ||
c.Volume.Number == Parser.SpecialVolumeNumber ? 1 : 0)
.ThenBy(c =>
// Priority 2: Loose leaf over specials
c.Volume.Number == Parser.SpecialVolumeNumber ? 1 : 0)
.ThenBy(c =>
// Priority 3: Non-special chapters
c.IsSpecial ? 1 : 0)
.ThenBy(c => c.Volume.Number)
.ThenBy(c => c.SortOrder)
.ApplyDefaultChapterOrdering()
.AsNoTracking()
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.FirstOrDefaultAsync();
if (firstChapter == null) return null;
var dto = _mapper.Map<ChapterDto>(firstChapter);
await AddChapterModifiers(userId, dto);
return dto;
}
public async Task<ChapterDto?> GetFirstChapterForVolumeAsync(int volumeId, int userId)
{
// Get the chapter entity with proper ordering
var firstChapter = await _context.Chapter
return await _context.Chapter
.Include(c => c.Volume)
.Include(c => c.Files)
.Where(c => c.Volume.Id == volumeId)
.OrderBy(c =>
// Priority 1: Regular volumes (not loose leaf, not special)
c.Volume.Number == Parser.LooseLeafVolumeNumber ||
c.Volume.Number == Parser.SpecialVolumeNumber ? 1 : 0)
.ThenBy(c =>
// Priority 2: Loose leaf over specials
c.Volume.Number == Parser.SpecialVolumeNumber ? 1 : 0)
.ThenBy(c =>
// Priority 3: Non-special chapters
c.IsSpecial ? 1 : 0)
.ThenBy(c => c.Volume.Number)
.ThenBy(c => c.SortOrder)
.ApplyDefaultChapterOrdering()
.AsNoTracking()
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.FirstOrDefaultAsync();
}
if (firstChapter == null) return null;
public async Task<IList<ChapterDto>> GetChapterDtosAsync(IEnumerable<int> chapterIds, int userId)
{
var chapterIdList = chapterIds.ToList();
if (chapterIdList.Count == 0) return [];
var dto = _mapper.Map<ChapterDto>(firstChapter);
await AddChapterModifiers(userId, dto);
return dto;
return await _context.Chapter
.Where(c => chapterIdList.Contains(c.Id))
.ProjectToWithProgress<Chapter, ChapterDto>(_mapper, userId)
.ToListAsync();
}
}