Merge branch 'develop' of https://github.com/Kareadita/Kavita into feature/magazine

This commit is contained in:
Joseph Milazzo 2025-05-04 09:20:15 -05:00
commit 08a32a26bc
283 changed files with 8056 additions and 2679 deletions

View file

@ -5,8 +5,10 @@ using System.Threading.Tasks;
using API.DTOs;
using API.DTOs.Metadata;
using API.DTOs.Reader;
using API.DTOs.SeriesDetail;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
@ -24,7 +26,9 @@ public enum ChapterIncludes
Files = 4,
People = 8,
Genres = 16,
Tags = 32
Tags = 32,
ExternalReviews = 1 << 6,
ExternalRatings = 1 << 7
}
public interface IChapterRepository
@ -48,6 +52,11 @@ public interface IChapterRepository
Task<ChapterDto> AddChapterModifiers(int userId, ChapterDto chapter);
IEnumerable<Chapter> GetChaptersForSeries(int seriesId);
Task<IList<Chapter>> GetAllChaptersForSeries(int seriesId);
Task<int> GetAverageUserRating(int chapterId, int userId);
Task<IList<UserReviewDto>> GetExternalChapterReviewDtos(int chapterId);
Task<IList<ExternalReview>> GetExternalChapterReview(int chapterId);
Task<IList<RatingDto>> GetExternalChapterRatingDtos(int chapterId);
Task<IList<ExternalRating>> GetExternalChapterRatings(int chapterId);
}
public class ChapterRepository : IChapterRepository
{
@ -310,4 +319,55 @@ public class ChapterRepository : IChapterRepository
.ThenInclude(cp => cp.Person)
.ToListAsync();
}
public async Task<int> GetAverageUserRating(int chapterId, int userId)
{
// If there is 0 or 1 rating and that rating is you, return 0 back
var countOfRatingsThatAreUser = await _context.AppUserChapterRating
.Where(r => r.ChapterId == chapterId && r.HasBeenRated)
.CountAsync(u => u.AppUserId == userId);
if (countOfRatingsThatAreUser == 1)
{
return 0;
}
var avg = (await _context.AppUserChapterRating
.Where(r => r.ChapterId == chapterId && r.HasBeenRated)
.AverageAsync(r => (int?) r.Rating));
return avg.HasValue ? (int) (avg.Value * 20) : 0;
}
public async Task<IList<UserReviewDto>> GetExternalChapterReviewDtos(int chapterId)
{
return await _context.Chapter
.Where(c => c.Id == chapterId)
.SelectMany(c => c.ExternalReviews)
// Don't use ProjectTo, it fails to map int to float (??)
.Select(r => _mapper.Map<UserReviewDto>(r))
.ToListAsync();
}
public async Task<IList<ExternalReview>> GetExternalChapterReview(int chapterId)
{
return await _context.Chapter
.Where(c => c.Id == chapterId)
.SelectMany(c => c.ExternalReviews)
.ToListAsync();
}
public async Task<IList<RatingDto>> GetExternalChapterRatingDtos(int chapterId)
{
return await _context.Chapter
.Where(c => c.Id == chapterId)
.SelectMany(c => c.ExternalRatings)
.ProjectTo<RatingDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
public async Task<IList<ExternalRating>> GetExternalChapterRatings(int chapterId)
{
return await _context.Chapter
.Where(c => c.Id == chapterId)
.SelectMany(c => c.ExternalRatings)
.ToListAsync();
}
}

View file

@ -13,6 +13,7 @@ using API.DTOs.CollectionTags;
using API.DTOs.Dashboard;
using API.DTOs.Filtering;
using API.DTOs.Filtering.v2;
using API.DTOs.KavitaPlus.Metadata;
using API.DTOs.Metadata;
using API.DTOs.ReadingLists;
using API.DTOs.Recommendation;
@ -57,6 +58,8 @@ public enum SeriesIncludes
ExternalRatings = 128,
ExternalRecommendations = 256,
ExternalMetadata = 512,
ExternalData = ExternalMetadata | ExternalReviews | ExternalRatings | ExternalRecommendations,
}
/// <summary>
@ -563,7 +566,13 @@ public class SeriesRepository : ISeriesRepository
if (!fullSeries) return await query.ToListAsync();
return await query.Include(s => s.Volumes)
return await query
.Include(s => s.Volumes)
.ThenInclude(v => v.Chapters)
.ThenInclude(c => c.ExternalRatings)
.Include(s => s.Volumes)
.ThenInclude(v => v.Chapters)
.ThenInclude(c => c.ExternalReviews)
.Include(s => s.Relations)
.Include(s => s.Metadata)

View file

@ -42,7 +42,8 @@ public enum AppUserIncludes
DashboardStreams = 2048,
SideNavStreams = 4096,
ExternalSources = 8192,
Collections = 16384 // 2^14
Collections = 16384, // 2^14
ChapterRatings = 1 << 15,
}
public interface IUserRepository
@ -65,7 +66,9 @@ public interface IUserRepository
Task<bool> IsUserAdminAsync(AppUser? user);
Task<IList<string>> GetRoles(int userId);
Task<AppUserRating?> GetUserRatingAsync(int seriesId, int userId);
Task<AppUserChapterRating?> GetUserChapterRatingAsync(int userId, int chapterId);
Task<IList<UserReviewDto>> GetUserRatingDtosForSeriesAsync(int seriesId, int userId);
Task<IList<UserReviewDto>> GetUserRatingDtosForChapterAsync(int chapterId, int userId);
Task<AppUserPreferences?> GetPreferencesAsync(string username);
Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForSeries(int userId, int seriesId);
Task<IEnumerable<BookmarkDto>> GetBookmarkDtosForVolume(int userId, int volumeId);
@ -587,7 +590,14 @@ public class UserRepository : IUserRepository
{
return await _context.AppUserRating
.Where(r => r.SeriesId == seriesId && r.AppUserId == userId)
.SingleOrDefaultAsync();
.FirstOrDefaultAsync();
}
public async Task<AppUserChapterRating?> GetUserChapterRatingAsync(int userId, int chapterId)
{
return await _context.AppUserChapterRating
.Where(r => r.AppUserId == userId && r.ChapterId == chapterId)
.FirstOrDefaultAsync();
}
public async Task<IList<UserReviewDto>> GetUserRatingDtosForSeriesAsync(int seriesId, int userId)
@ -603,6 +613,19 @@ public class UserRepository : IUserRepository
.ToListAsync();
}
public async Task<IList<UserReviewDto>> GetUserRatingDtosForChapterAsync(int chapterId, int userId)
{
return await _context.AppUserChapterRating
.Include(r => r.AppUser)
.Where(r => r.ChapterId == chapterId)
.Where(r => r.AppUser.UserPreferences.ShareReviews || r.AppUserId == userId)
.OrderBy(r => r.AppUserId == userId)
.ThenBy(r => r.Rating)
.AsSplitQuery()
.ProjectTo<UserReviewDto>(_mapper.ConfigurationProvider)
.ToListAsync();
}
public async Task<AppUserPreferences?> GetPreferencesAsync(string username)
{
return await _context.AppUserPreferences