Browse by Genre/Tag/Person with new metadata system for People (#3835)
Co-authored-by: Stepan Goremykin <s.goremykin@proton.me> Co-authored-by: goremykin <goremukin@gmail.com> Co-authored-by: Christopher <39032787+MrRobotjs@users.noreply.github.com> Co-authored-by: Fesaa <77553571+Fesaa@users.noreply.github.com>
This commit is contained in:
parent
00c4712fc3
commit
c52ed1f65d
147 changed files with 6612 additions and 958 deletions
|
|
@ -10,11 +10,9 @@ using API.Entities.Interfaces;
|
|||
using API.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetVips;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using SixLabors.ImageSharp.Processing.Processors.Quantization;
|
||||
using Color = System.Drawing.Color;
|
||||
using Image = NetVips.Image;
|
||||
|
||||
namespace API.Services;
|
||||
|
|
@ -750,7 +748,7 @@ public class ImageService : IImageService
|
|||
}
|
||||
|
||||
|
||||
public static Color HexToRgb(string? hex)
|
||||
public static (int R, int G, int B) HexToRgb(string? hex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hex)) throw new ArgumentException("Hex cannot be null");
|
||||
|
||||
|
|
@ -774,7 +772,7 @@ public class ImageService : IImageService
|
|||
var g = Convert.ToInt32(hex.Substring(2, 2), 16);
|
||||
var b = Convert.ToInt32(hex.Substring(4, 2), 16);
|
||||
|
||||
return Color.FromArgb(r, g, b);
|
||||
return (r, g, b);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,9 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
/// <summary>
|
||||
/// Returns the match results for a Series from UI Flow
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Will extract alternative names like Localized name, year will send as ReleaseYear but fallback to Comic Vine syntax if applicable
|
||||
/// </remarks>
|
||||
/// <param name="dto"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IList<ExternalSeriesMatchDto>> MatchSeries(MatchSeriesDto dto)
|
||||
|
|
@ -212,19 +215,26 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
var potentialAnilistId = ScrobblingService.ExtractId<int?>(dto.Query, ScrobblingService.AniListWeblinkWebsite);
|
||||
var potentialMalId = ScrobblingService.ExtractId<long?>(dto.Query, ScrobblingService.MalWeblinkWebsite);
|
||||
|
||||
List<string> altNames = [series.LocalizedName, series.OriginalName];
|
||||
if (potentialAnilistId == null && potentialMalId == null && !string.IsNullOrEmpty(dto.Query))
|
||||
var format = series.Library.Type.ConvertToPlusMediaFormat(series.Format);
|
||||
var otherNames = ExtractAlternativeNames(series);
|
||||
|
||||
var year = series.Metadata.ReleaseYear;
|
||||
if (year == 0 && format == PlusMediaFormat.Comic && !string.IsNullOrWhiteSpace(series.Name))
|
||||
{
|
||||
altNames.Add(dto.Query);
|
||||
var potentialYear = Parser.ParseYear(series.Name);
|
||||
if (!string.IsNullOrEmpty(potentialYear))
|
||||
{
|
||||
year = int.Parse(potentialYear);
|
||||
}
|
||||
}
|
||||
|
||||
var matchRequest = new MatchSeriesRequestDto()
|
||||
{
|
||||
Format = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
|
||||
Format = format,
|
||||
Query = dto.Query,
|
||||
SeriesName = series.Name,
|
||||
AlternativeNames = altNames.Where(s => !string.IsNullOrEmpty(s)).ToList(),
|
||||
Year = series.Metadata.ReleaseYear,
|
||||
AlternativeNames = otherNames,
|
||||
Year = year,
|
||||
AniListId = potentialAnilistId ?? ScrobblingService.GetAniListId(series),
|
||||
MalId = potentialMalId ?? ScrobblingService.GetMalId(series)
|
||||
};
|
||||
|
|
@ -254,6 +264,12 @@ public class ExternalMetadataService : IExternalMetadataService
|
|||
return ArraySegment<ExternalSeriesMatchDto>.Empty;
|
||||
}
|
||||
|
||||
private static List<string> ExtractAlternativeNames(Series series)
|
||||
{
|
||||
List<string> altNames = [series.LocalizedName, series.OriginalName];
|
||||
return altNames.Where(s => !string.IsNullOrEmpty(s)).Distinct().ToList();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves Metadata about a Recommended External Series
|
||||
|
|
|
|||
|
|
@ -130,22 +130,23 @@ public class LicenseService(
|
|||
if (cacheValue.HasValue) return cacheValue.Value;
|
||||
}
|
||||
|
||||
var result = false;
|
||||
try
|
||||
{
|
||||
var serverSetting = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey);
|
||||
var result = await IsLicenseValid(serverSetting.Value);
|
||||
await provider.FlushAsync();
|
||||
await provider.SetAsync(CacheKey, result, _licenseCacheTimeout);
|
||||
return result;
|
||||
result = await IsLicenseValid(serverSetting.Value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "There was an issue connecting to Kavita+");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await provider.FlushAsync();
|
||||
await provider.SetAsync(CacheKey, false, _licenseCacheTimeout);
|
||||
await provider.SetAsync(CacheKey, result, _licenseCacheTimeout);
|
||||
}
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -432,6 +432,7 @@ public class ReadingProfileService(IUnitOfWork unitOfWork, ILocalizationService
|
|||
existingProfile.SwipeToPaginate = dto.SwipeToPaginate;
|
||||
existingProfile.AllowAutomaticWebtoonReaderDetection = dto.AllowAutomaticWebtoonReaderDetection;
|
||||
existingProfile.WidthOverride = dto.WidthOverride;
|
||||
existingProfile.DisableWidthOverride = dto.DisableWidthOverride;
|
||||
|
||||
// Book Reader
|
||||
existingProfile.BookReaderMargin = dto.BookReaderMargin;
|
||||
|
|
|
|||
|
|
@ -206,17 +206,12 @@ public class CoverDbService : ICoverDbService
|
|||
throw new KavitaException($"Could not grab publisher image for {publisherName}");
|
||||
}
|
||||
|
||||
_logger.LogTrace("Fetching publisher image from {Url}", publisherLink.Sanitize());
|
||||
// Download the publisher file using Flurl
|
||||
var publisherStream = await publisherLink
|
||||
.AllowHttpStatus("2xx,304")
|
||||
.GetStreamAsync();
|
||||
|
||||
// Create the destination file path
|
||||
using var image = Image.NewFromStream(publisherStream);
|
||||
var filename = ImageService.GetPublisherFormat(publisherName, encodeFormat);
|
||||
|
||||
image.WriteToFile(Path.Combine(_directoryService.PublisherDirectory, filename));
|
||||
_logger.LogTrace("Fetching publisher image from {Url}", publisherLink.Sanitize());
|
||||
await DownloadImageFromUrl(publisherName, encodeFormat, publisherLink, _directoryService.PublisherDirectory);
|
||||
|
||||
_logger.LogDebug("Publisher image for {PublisherName} downloaded and saved successfully", publisherName.Sanitize());
|
||||
|
||||
return filename;
|
||||
|
|
@ -302,7 +297,27 @@ public class CoverDbService : ICoverDbService
|
|||
.GetStreamAsync();
|
||||
|
||||
using var image = Image.NewFromStream(imageStream);
|
||||
image.WriteToFile(targetFile);
|
||||
try
|
||||
{
|
||||
image.WriteToFile(targetFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
switch (encodeFormat)
|
||||
{
|
||||
case EncodeFormat.PNG:
|
||||
image.Pngsave(Path.Combine(_directoryService.FaviconDirectory, filename));
|
||||
break;
|
||||
case EncodeFormat.WEBP:
|
||||
image.Webpsave(Path.Combine(_directoryService.FaviconDirectory, filename));
|
||||
break;
|
||||
case EncodeFormat.AVIF:
|
||||
image.Heifsave(Path.Combine(_directoryService.FaviconDirectory, filename));
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(encodeFormat), encodeFormat, null);
|
||||
}
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
|
@ -385,14 +400,13 @@ public class CoverDbService : ICoverDbService
|
|||
private async Task<string> FallbackToKavitaReaderPublisher(string publisherName)
|
||||
{
|
||||
const string publisherFileName = "publishers.txt";
|
||||
var externalLink = string.Empty;
|
||||
var allOverrides = await GetCachedData(publisherFileName) ??
|
||||
await $"{NewHost}publishers/{publisherFileName}".GetStringAsync();
|
||||
|
||||
// Cache immediately
|
||||
await CacheDataAsync(publisherFileName, allOverrides);
|
||||
|
||||
if (string.IsNullOrEmpty(allOverrides)) return externalLink;
|
||||
if (string.IsNullOrEmpty(allOverrides)) return string.Empty;
|
||||
|
||||
var externalFile = allOverrides
|
||||
.Split("\n")
|
||||
|
|
@ -415,7 +429,7 @@ public class CoverDbService : ICoverDbService
|
|||
throw new KavitaException($"Could not grab publisher image for {publisherName}");
|
||||
}
|
||||
|
||||
return $"{NewHost}publishers/{externalLink}";
|
||||
return $"{NewHost}publishers/{externalFile}";
|
||||
}
|
||||
|
||||
private async Task CacheDataAsync(string fileName, string? content)
|
||||
|
|
@ -572,8 +586,7 @@ public class CoverDbService : ICoverDbService
|
|||
var choseNewImage = string.Equals(betterImage, tempFullPath, StringComparison.OrdinalIgnoreCase);
|
||||
if (choseNewImage)
|
||||
{
|
||||
|
||||
// Don't delete series cover, unless it's an override, otherwise the first chapter cover will be null
|
||||
// Don't delete the Series cover unless it is an override, otherwise the first chapter will be null
|
||||
if (existingPath.Contains(ImageService.GetSeriesFormat(series.Id)))
|
||||
{
|
||||
_directoryService.DeleteFiles([existingPath]);
|
||||
|
|
@ -624,6 +637,7 @@ public class CoverDbService : ICoverDbService
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Refactor this to IHasCoverImage instead of a hard entity type
|
||||
public async Task SetChapterCoverByUrl(Chapter chapter, string url, bool fromBase64 = true, bool chooseBetterImage = false)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
|
|
|
|||
|
|
@ -1159,6 +1159,12 @@ public static partial class Parser
|
|||
return !string.IsNullOrEmpty(name) && SeriesAndYearRegex.IsMatch(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Year from a Comic Series: Series Name (YEAR)
|
||||
/// </summary>
|
||||
/// <example>Harley Quinn (2024) returns 2024</example>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public static string ParseYear(string? name)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name)) return string.Empty;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue