Bugfixes + Potential iOS Webtoon Reader Fix (#2650)

This commit is contained in:
Joe Milazzo 2024-01-25 11:09:44 -06:00 committed by GitHub
parent 56fa393cf0
commit f660a1cd06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 157 additions and 197 deletions

View file

@ -7,12 +7,8 @@ using System.Net;
using System.Threading.Tasks;
using API.Data;
using API.DTOs.Email;
using API.Entities.Enums;
using Flurl.Http;
using Kavita.Common;
using Kavita.Common.EnvironmentInfo;
using MailKit.Security;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MimeKit;
@ -38,7 +34,6 @@ public interface IEmailService
Task<bool> SendForgotPasswordEmail(PasswordResetEmailDto dto);
Task<bool> SendFilesToEmail(SendToDto data);
Task<EmailTestResultDto> SendTestEmail(string adminEmail);
Task<bool> IsDefaultEmailService();
Task SendEmailChangeEmail(ConfirmationEmailDto data);
bool IsValidEmail(string email);
}
@ -47,7 +42,6 @@ public class EmailService : IEmailService
{
private readonly ILogger<EmailService> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly IDownloadService _downloadService;
private readonly IDirectoryService _directoryService;
private const string TemplatePath = @"{0}.html";
@ -57,11 +51,10 @@ public class EmailService : IEmailService
public const string DefaultApiUrl = "https://email.kavitareader.com";
public EmailService(ILogger<EmailService> logger, IUnitOfWork unitOfWork, IDownloadService downloadService, IDirectoryService directoryService)
public EmailService(ILogger<EmailService> logger, IUnitOfWork unitOfWork, IDirectoryService directoryService)
{
_logger = logger;
_unitOfWork = unitOfWork;
_downloadService = downloadService;
_directoryService = directoryService;
}
@ -114,14 +107,6 @@ public class EmailService : IEmailService
return result;
}
[Obsolete]
public async Task<bool> IsDefaultEmailService()
{
return (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.EmailServiceUrl))!.Value!
.Equals(DefaultApiUrl);
}
/// <summary>
/// Sends an email that has a link that will finalize an Email Change
/// </summary>

View file

@ -36,7 +36,7 @@ internal class ExternalMetadataIdsDto
public MediaFormat? PlusMediaFormat { get; set; } = MediaFormat.Unknown;
}
internal class SeriesDetailPlusAPIDto
internal class SeriesDetailPlusApiDto
{
public IEnumerable<MediaRecommendationDto> Recommendations { get; set; }
public IEnumerable<UserReviewDto> Reviews { get; set; }
@ -108,7 +108,7 @@ public class ExternalMetadataService : IExternalMetadataService
if (!needsRefresh)
{
// Convert into DTOs and return
return await SerializeExternalSeriesDetail(seriesId, externalSeriesMetadata, user, series);
return await SerializeExternalSeriesDetail(seriesId, series.LibraryId, user);
}
try
@ -123,7 +123,7 @@ public class ExternalMetadataService : IExternalMetadataService
.WithHeader("Content-Type", "application/json")
.WithTimeout(TimeSpan.FromSeconds(Configuration.DefaultTimeOutSecs))
.PostJsonAsync(new PlusSeriesDtoBuilder(series).Build())
.ReceiveJson<SeriesDetailPlusAPIDto>();
.ReceiveJson<SeriesDetailPlusApiDto>();
// Clear out existing results
@ -149,7 +149,7 @@ public class ExternalMetadataService : IExternalMetadataService
// Recommendations
externalSeriesMetadata.ExternalRecommendations ??= new List<ExternalRecommendation>();
var recs = await ProcessRecommendations(series, user!, result.Recommendations, externalSeriesMetadata);
var recs = await ProcessRecommendations(series, user, result.Recommendations, externalSeriesMetadata);
externalSeriesMetadata.LastUpdatedUtc = DateTime.UtcNow;
externalSeriesMetadata.AverageExternalRating = (int) externalSeriesMetadata.ExternalRatings
@ -161,14 +161,12 @@ public class ExternalMetadataService : IExternalMetadataService
await _unitOfWork.CommitAsync();
var ret = new SeriesDetailPlusDto()
return new SeriesDetailPlusDto()
{
Recommendations = recs,
Ratings = result.Ratings,
Reviews = result.Reviews
};
return ret;
}
catch (FlurlHttpException ex)
{
@ -185,46 +183,9 @@ public class ExternalMetadataService : IExternalMetadataService
return null;
}
private async Task<SeriesDetailPlusDto?> SerializeExternalSeriesDetail(int seriesId, ExternalSeriesMetadata externalSeriesMetadata,
AppUser user, Series series)
private async Task<SeriesDetailPlusDto?> SerializeExternalSeriesDetail(int seriesId, int libraryId, AppUser user)
{
var seriesIdsOnServer = externalSeriesMetadata.ExternalRecommendations
.Where(r => r.SeriesId is > 0)
.Select(s => (int) s.SeriesId!)
.ToList();
var ownedSeries = (await _unitOfWork.SeriesRepository.GetSeriesDtoForIdsAsync(seriesIdsOnServer, user.Id))
.ToList();
var canSeeExternalSeries = user is {AgeRestriction: AgeRating.NotApplicable} &&
await _unitOfWork.UserRepository.IsUserAdminAsync(user);
var externalSeries = new List<ExternalSeriesDto>();
if (canSeeExternalSeries)
{
externalSeries = externalSeriesMetadata.ExternalRecommendations
.Where(r => r.SeriesId is null or 0)
.Select(r => _mapper.Map<ExternalSeriesDto>(r))
.ToList();
}
var ret = await _unitOfWork.ExternalSeriesMetadataRepository.GetSeriesDetailPlusDto(seriesId, series.LibraryId, user);
return new SeriesDetailPlusDto()
{
Ratings = externalSeriesMetadata.ExternalRatings.Select(r => _mapper.Map<RatingDto>(r)),
Reviews = externalSeriesMetadata.ExternalReviews.OrderByDescending(r => r.Score).Select(r =>
{
var review = _mapper.Map<UserReviewDto>(r);
review.SeriesId = seriesId;
review.LibraryId = series.LibraryId;
review.IsExternal = true;
return review;
}),
Recommendations = new RecommendationDto()
{
ExternalSeries = externalSeries,
OwnedSeries = ownedSeries
}
};
return await _unitOfWork.ExternalSeriesMetadataRepository.GetSeriesDetailPlusDto(seriesId, libraryId, user);
}
private async Task<ExternalSeriesMetadata> GetExternalSeriesMetadataForSeries(int seriesId, Series series)
@ -249,8 +210,6 @@ public class ExternalMetadataService : IExternalMetadataService
OwnedSeries = new List<SeriesDto>()
};
var canSeeExternalSeries = user is {AgeRestriction: AgeRating.NotApplicable} &&
await _unitOfWork.UserRepository.IsUserAdminAsync(user);
// NOTE: This can result in a series being recommended that shares the same name but different format
foreach (var rec in recs)
{
@ -276,7 +235,6 @@ public class ExternalMetadataService : IExternalMetadataService
continue;
}
if (!canSeeExternalSeries) continue;
// We can show this based on user permissions
if (string.IsNullOrEmpty(rec.Name) || string.IsNullOrEmpty(rec.SiteUrl) || string.IsNullOrEmpty(rec.CoverUrl)) continue;
recDto.ExternalSeries.Add(new ExternalSeriesDto()

View file

@ -32,7 +32,7 @@ namespace API.Services;
public interface ISeriesService
{
Task<SeriesDetailDto> GetSeriesDetail(int seriesId, int userId);
Task<bool> UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto, int userId = 0);
Task<bool> UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto);
Task<bool> UpdateRating(AppUser user, UpdateSeriesRatingDto updateSeriesRatingDto);
Task<bool> DeleteMultipleSeries(IList<int> seriesIds);
Task<bool> UpdateRelatedSeries(UpdateRelatedSeriesDto dto);
@ -111,9 +111,8 @@ public class SeriesService : ISeriesService
/// Updates the Series Metadata.
/// </summary>
/// <param name="updateSeriesMetadataDto"></param>
/// <param name="userId">If 0, does not bust any cache</param>
/// <returns></returns>
public async Task<bool> UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto, int userId = 0)
public async Task<bool> UpdateSeriesMetadata(UpdateSeriesMetadataDto updateSeriesMetadataDto)
{
var hasWebLinksChanged = false;
try
@ -315,10 +314,10 @@ public class SeriesService : ISeriesService
_logger.LogError(ex, "There was an issue cleaning up DB entries. This may happen if Komf is spamming updates. Nightly cleanup will work");
}
if (hasWebLinksChanged && userId > 0)
if (hasWebLinksChanged)
{
_logger.LogDebug("Clearing cache as series weblinks may have changed");
await _cacheProvider.RemoveAsync(MetadataController.CacheKey + seriesId + userId);
await _cacheProvider.RemoveAsync(MetadataController.CacheKey + seriesId);
}

View file

@ -543,7 +543,7 @@ public static class Parser
{
// Historys Strongest Disciple Kenichi_v11_c90-98.zip, ...c90.5-100.5
new Regex(
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)(-\d+(\.\d)?)?)",
@"(\b|_)(c|ch)(\.?\s?)(?<Chapter>(\d+(\.\d)?)(-c?\d+(\.\d)?)?)",
MatchOptions, RegexTimeout),
// [Suihei Kiki]_Kasumi_Otoko_no_Ko_[Taruby]_v1.1.zip
new Regex(
@ -761,6 +761,11 @@ public static class Parser
var from = RemoveLeadingZeroes(tokens[0]);
if (tokens.Length != 2) return from;
// Occasionally users will use c01-c02 instead of c01-02, clean any leftover c
if (tokens[1].StartsWith("c", StringComparison.InvariantCultureIgnoreCase))
{
tokens[1] = tokens[1].Replace("c", string.Empty, StringComparison.InvariantCultureIgnoreCase);
}
var to = RemoveLeadingZeroes(hasPart ? AddChapterPart(tokens[1]) : tokens[1]);
return $"{from}-{to}";
}

View file

@ -752,6 +752,8 @@ public class ProcessSeries : IProcessSeries
.Where(s => !string.IsNullOrEmpty(s))
.Select(s => s.Trim())
);
// For each weblink, try to parse out some MetadataIds and store in the Chapter directly for matching (CBL)
}
if (!string.IsNullOrEmpty(comicInfo.Isbn))

View file

@ -48,6 +48,7 @@ public interface IVersionUpdaterService
Task<UpdateNotificationDto?> CheckForUpdate();
Task PushUpdate(UpdateNotificationDto update);
Task<IEnumerable<UpdateNotificationDto>> GetAllReleases();
Task<int> GetNumberOfReleasesBehind();
}
public class VersionUpdaterService : IVersionUpdaterService
@ -87,6 +88,12 @@ public class VersionUpdaterService : IVersionUpdaterService
return updates.Select(CreateDto).Where(d => d != null)!;
}
public async Task<int> GetNumberOfReleasesBehind()
{
var updates = await GetAllReleases();
return updates.TakeWhile(update => update.UpdateVersion != update.CurrentVersion).Count();
}
private UpdateNotificationDto? CreateDto(GithubReleaseMetadata? update)
{
if (update == null || string.IsNullOrEmpty(update.Tag_Name)) return null;