(Kavita+) External Series Detail (#2309)
This commit is contained in:
parent
bd62e00ec5
commit
6067c9233c
32 changed files with 2354 additions and 726 deletions
|
|
@ -22,4 +22,5 @@ public static class EasyCacheProfiles
|
|||
public const string KavitaPlusReviews = "kavita+reviews";
|
||||
public const string KavitaPlusRecommendations = "kavita+recommendations";
|
||||
public const string KavitaPlusRatings = "kavita+ratings";
|
||||
public const string KavitaPlusExternalSeries = "kavita+externalSeries";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1022,7 +1022,8 @@ public class OpdsController : BaseApiController
|
|||
/// <param name="pageNumber"></param>
|
||||
/// <returns></returns>
|
||||
[HttpGet("{apiKey}/image")]
|
||||
public async Task<ActionResult> GetPageStreamedImage(string apiKey, [FromQuery] int libraryId, [FromQuery] int seriesId, [FromQuery] int volumeId,[FromQuery] int chapterId, [FromQuery] int pageNumber)
|
||||
public async Task<ActionResult> GetPageStreamedImage(string apiKey, [FromQuery] int libraryId, [FromQuery] int seriesId,
|
||||
[FromQuery] int volumeId,[FromQuery] int chapterId, [FromQuery] int pageNumber)
|
||||
{
|
||||
var userId = await GetUser(apiKey);
|
||||
if (pageNumber < 0) return BadRequest(await _localizationService.Translate(userId, "greater-0", "Page"));
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using API.DTOs.Dashboard;
|
|||
using API.DTOs.Filtering;
|
||||
using API.DTOs.Filtering.v2;
|
||||
using API.DTOs.Metadata;
|
||||
using API.DTOs.Recommendation;
|
||||
using API.DTOs.SeriesDetail;
|
||||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
|
|
@ -35,14 +36,17 @@ public class SeriesController : BaseApiController
|
|||
private readonly ISeriesService _seriesService;
|
||||
private readonly ILicenseService _licenseService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IExternalMetadataService _externalMetadataService;
|
||||
private readonly IEasyCachingProvider _ratingCacheProvider;
|
||||
private readonly IEasyCachingProvider _reviewCacheProvider;
|
||||
private readonly IEasyCachingProvider _recommendationCacheProvider;
|
||||
private readonly IEasyCachingProvider _externalSeriesCacheProvider;
|
||||
public const string CacheKey = "recommendation_";
|
||||
|
||||
|
||||
public SeriesController(ILogger<SeriesController> logger, ITaskScheduler taskScheduler, IUnitOfWork unitOfWork,
|
||||
ISeriesService seriesService, ILicenseService licenseService,
|
||||
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService)
|
||||
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService, IExternalMetadataService externalMetadataService)
|
||||
{
|
||||
_logger = logger;
|
||||
_taskScheduler = taskScheduler;
|
||||
|
|
@ -50,10 +54,12 @@ public class SeriesController : BaseApiController
|
|||
_seriesService = seriesService;
|
||||
_licenseService = licenseService;
|
||||
_localizationService = localizationService;
|
||||
_externalMetadataService = externalMetadataService;
|
||||
|
||||
_ratingCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.KavitaPlusRatings);
|
||||
_reviewCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.KavitaPlusReviews);
|
||||
_recommendationCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.KavitaPlusRecommendations);
|
||||
_externalSeriesCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.KavitaPlusExternalSeries);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -576,6 +582,32 @@ public class SeriesController : BaseApiController
|
|||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-relationship"));
|
||||
}
|
||||
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("external-series-detail")]
|
||||
public async Task<ActionResult<ExternalSeriesDto>> GetExternalSeriesInfo(int? aniListId, long? malId)
|
||||
{
|
||||
if (!await _licenseService.HasActiveLicense())
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
var cacheKey = $"{CacheKey}-{aniListId ?? 0}-{malId ?? 0}";
|
||||
var results = await _externalSeriesCacheProvider.GetAsync<ExternalSeriesDto>(cacheKey);
|
||||
if (results.HasValue)
|
||||
{
|
||||
return Ok(results.Value);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var ret = await _externalMetadataService.GetExternalSeriesDetail(aniListId, malId);
|
||||
await _externalSeriesCacheProvider.SetAsync(cacheKey, ret, TimeSpan.FromMinutes(15));
|
||||
return Ok(ret);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest("Unable to load External Series details");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
20
API/DTOs/Recommendation/ExternalSeriesDetailDto.cs
Normal file
20
API/DTOs/Recommendation/ExternalSeriesDetailDto.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace API.DTOs.Recommendation;
|
||||
|
||||
public class ExternalSeriesDetailDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int? AniListId { get; set; }
|
||||
public long? MALId { get; set; }
|
||||
public IList<string> Synonyms { get; set; }
|
||||
public PlusMediaFormat PlusMediaFormat { get; set; }
|
||||
public string? SiteUrl { get; set; }
|
||||
public string? CoverUrl { get; set; }
|
||||
public IList<string> Genres { get; set; }
|
||||
public IList<SeriesStaffDto> Staff { get; set; }
|
||||
public IList<MetadataTagDto> Tags { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int? VolumeCount { get; set; }
|
||||
public int? ChapterCount { get; set; }
|
||||
}
|
||||
|
|
@ -7,4 +7,6 @@ public class ExternalSeriesDto
|
|||
public required string CoverUrl { get; set; }
|
||||
public required string Url { get; set; }
|
||||
public string? Summary { get; set; }
|
||||
public int? AniListId { get; set; }
|
||||
public long? MalId { get; set; }
|
||||
}
|
||||
|
|
|
|||
11
API/DTOs/Recommendation/MetadataTagDto.cs
Normal file
11
API/DTOs/Recommendation/MetadataTagDto.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace API.DTOs.Recommendation;
|
||||
|
||||
public class MetadataTagDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Description { get; private set; }
|
||||
public int? Rank { get; private set; }
|
||||
public bool IsGeneralSpoiler { get; private set; }
|
||||
public bool IsMediaSpoiler { get; private set; }
|
||||
public bool IsAdult { get; private set; }
|
||||
}
|
||||
15
API/DTOs/Recommendation/PlusMediaFormat.cs
Normal file
15
API/DTOs/Recommendation/PlusMediaFormat.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.ComponentModel;
|
||||
|
||||
namespace API.DTOs.Recommendation;
|
||||
|
||||
public enum PlusMediaFormat
|
||||
{
|
||||
[Description("Manga")]
|
||||
Manga = 1,
|
||||
[Description("Comic")]
|
||||
Comic = 2,
|
||||
[Description("LightNovel")]
|
||||
LightNovel = 3,
|
||||
[Description("Book")]
|
||||
Book = 4
|
||||
}
|
||||
11
API/DTOs/Recommendation/SeriesStaffDto.cs
Normal file
11
API/DTOs/Recommendation/SeriesStaffDto.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
namespace API.DTOs.Recommendation;
|
||||
|
||||
public class SeriesStaffDto
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
public required string Url { get; set; }
|
||||
public required string Role { get; set; }
|
||||
public string? ImageUrl { get; set; }
|
||||
public string? Gender { get; set; }
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ using Microsoft.AspNetCore.Hosting;
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace API.Extensions;
|
||||
|
||||
|
|
@ -73,6 +72,7 @@ public static class ApplicationServiceExtensions
|
|||
services.AddScoped<ILicenseService, LicenseService>();
|
||||
services.AddScoped<IReviewService, ReviewService>();
|
||||
services.AddScoped<IRatingService, RatingService>();
|
||||
services.AddScoped<IExternalMetadataService, ExternalMetadataService>();
|
||||
|
||||
services.AddSqLite();
|
||||
services.AddSignalR(opt => opt.EnableDetailedErrors = true);
|
||||
|
|
@ -89,6 +89,7 @@ public static class ApplicationServiceExtensions
|
|||
options.UseInMemory(EasyCacheProfiles.KavitaPlusReviews);
|
||||
options.UseInMemory(EasyCacheProfiles.KavitaPlusRecommendations);
|
||||
options.UseInMemory(EasyCacheProfiles.KavitaPlusRatings);
|
||||
options.UseInMemory(EasyCacheProfiles.KavitaPlusExternalSeries);
|
||||
});
|
||||
|
||||
services.AddMemoryCache(options =>
|
||||
|
|
|
|||
|
|
@ -550,8 +550,12 @@ public class BookService : IBookService
|
|||
}
|
||||
}
|
||||
|
||||
// Check if there is a SortTitle
|
||||
|
||||
// If this is a single book and not a collection, set publication status to Completed
|
||||
if (string.IsNullOrEmpty(info.Volume) && Parser.ParseVolume(filePath).Equals(Parser.DefaultVolume))
|
||||
{
|
||||
info.Number = "1";
|
||||
info.Count = 1;
|
||||
}
|
||||
|
||||
// Include regular Writer as well, for cases where there is no special tag
|
||||
info.Writer = string.Join(",",
|
||||
|
|
|
|||
74
API/Services/Plus/ExternalMetadataService.cs
Normal file
74
API/Services/Plus/ExternalMetadataService.cs
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs.Recommendation;
|
||||
using API.Entities.Enums;
|
||||
using API.Helpers.Builders;
|
||||
using Flurl.Http;
|
||||
using Kavita.Common;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
using Kavita.Common.Helpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Services.Plus;
|
||||
|
||||
public interface IExternalMetadataService
|
||||
{
|
||||
Task<ExternalSeriesDetailDto> GetExternalSeriesDetail(int? aniListId, long? malId);
|
||||
}
|
||||
|
||||
public class ExternalMetadataService : IExternalMetadataService
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ILogger<ExternalMetadataService> _logger;
|
||||
|
||||
public ExternalMetadataService(IUnitOfWork unitOfWork, ILogger<ExternalMetadataService> logger)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
|
||||
FlurlHttp.ConfigureClient(Configuration.KavitaPlusApiUrl, cli =>
|
||||
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
|
||||
}
|
||||
|
||||
public async Task<ExternalSeriesDetailDto?> GetExternalSeriesDetail(int? aniListId, long? malId)
|
||||
{
|
||||
if (!aniListId.HasValue && !malId.HasValue)
|
||||
{
|
||||
throw new KavitaException("Unable to find valid information from url for External Load");
|
||||
}
|
||||
|
||||
var license = (await _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.LicenseKey)).Value;
|
||||
return await GetSeriesDetail(license, aniListId, malId);
|
||||
|
||||
}
|
||||
|
||||
private async Task<ExternalSeriesDetailDto?> GetSeriesDetail(string license, int? anilistId, long? malId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await (Configuration.KavitaPlusApiUrl + "/api/metadata/series/detail")
|
||||
.WithHeader("Accept", "application/json")
|
||||
.WithHeader("User-Agent", "Kavita")
|
||||
.WithHeader("x-license-key", license)
|
||||
.WithHeader("x-installId", HashUtil.ServerToken())
|
||||
.WithHeader("x-kavita-version", BuildInfo.Version)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithTimeout(TimeSpan.FromSeconds(Configuration.DefaultTimeOutSecs))
|
||||
.PostJsonAsync(new
|
||||
{
|
||||
AnilistId = anilistId,
|
||||
MalId = malId,
|
||||
})
|
||||
.ReceiveJson<ExternalSeriesDetailDto>();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, "An error happened during the request to Kavita+ API");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,9 +10,7 @@ using API.DTOs.Scrobbling;
|
|||
using API.Entities;
|
||||
using API.Entities.Enums;
|
||||
using API.Extensions;
|
||||
using API.Helpers;
|
||||
using API.Helpers.Builders;
|
||||
using API.Services.Tasks.Scanner.Parser;
|
||||
using Flurl.Http;
|
||||
using Kavita.Common;
|
||||
using Kavita.Common.EnvironmentInfo;
|
||||
|
|
@ -111,7 +109,9 @@ public class RecommendationService : IRecommendationService
|
|||
Name = string.IsNullOrEmpty(rec.Name) ? rec.RecommendationNames.First() : rec.Name,
|
||||
Url = rec.SiteUrl,
|
||||
CoverUrl = rec.CoverUrl,
|
||||
Summary = rec.Summary
|
||||
Summary = rec.Summary,
|
||||
AniListId = rec.AniListId,
|
||||
MalId = rec.MalId
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue