Added API benchmark, hash device_id, handled null cases in GET/PUT
This commit is contained in:
parent
e84aed357a
commit
f30e3b17d6
7 changed files with 79 additions and 11 deletions
|
|
@ -26,5 +26,10 @@
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Data\AesopsFables.epub">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
41
API.Benchmark/KoreaderHashBenchmark.cs
Normal file
41
API.Benchmark/KoreaderHashBenchmark.cs
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
using API.Helpers.Builders;
|
||||||
|
using BenchmarkDotNet.Attributes;
|
||||||
|
using BenchmarkDotNet.Order;
|
||||||
|
using System;
|
||||||
|
using API.Entities.Enums;
|
||||||
|
|
||||||
|
namespace API.Benchmark
|
||||||
|
{
|
||||||
|
[StopOnFirstError]
|
||||||
|
[MemoryDiagnoser]
|
||||||
|
[RankColumn]
|
||||||
|
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||||
|
[SimpleJob(launchCount: 1, warmupCount: 5, invocationCount: 20)]
|
||||||
|
public class KoreaderHashBenchmark
|
||||||
|
{
|
||||||
|
private const string sourceEpub = "./Data/AesopsFables.epub";
|
||||||
|
|
||||||
|
[Benchmark(Baseline = true)]
|
||||||
|
public void TestBuildManga_baseline()
|
||||||
|
{
|
||||||
|
var file = new MangaFileBuilder(sourceEpub, MangaFormat.Epub)
|
||||||
|
.Build();
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to build manga file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void TestBuildManga_withHash()
|
||||||
|
{
|
||||||
|
var file = new MangaFileBuilder(sourceEpub, MangaFormat.Epub)
|
||||||
|
.WithHash()
|
||||||
|
.Build();
|
||||||
|
if (file == null)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to build manga file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -77,6 +77,8 @@ public class KoreaderController : BaseApiController
|
||||||
|
|
||||||
[HttpGet("{apiKey}/syncs/progress/{ebookHash}")]
|
[HttpGet("{apiKey}/syncs/progress/{ebookHash}")]
|
||||||
public async Task<ActionResult<KoreaderBookDto>> GetProgress(string apiKey, string ebookHash)
|
public async Task<ActionResult<KoreaderBookDto>> GetProgress(string apiKey, string ebookHash)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var userId = await GetUserId(apiKey);
|
var userId = await GetUserId(apiKey);
|
||||||
var response = await _koreaderService.GetProgress(ebookHash, userId);
|
var response = await _koreaderService.GetProgress(ebookHash, userId);
|
||||||
|
|
@ -84,6 +86,13 @@ public class KoreaderController : BaseApiController
|
||||||
|
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
}
|
}
|
||||||
|
catch (KavitaException ex)
|
||||||
|
{
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<int> GetUserId(string apiKey)
|
private async Task<int> GetUserId(string apiKey)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using API.DTOs.Koreader;
|
using API.DTOs.Koreader;
|
||||||
|
|
||||||
namespace API.Helpers.Builders;
|
namespace API.Helpers.Builders;
|
||||||
|
|
@ -36,7 +38,9 @@ public class KoreaderBookDtoBuilder : IEntityBuilder<KoreaderBookDto>
|
||||||
|
|
||||||
public KoreaderBookDtoBuilder WithDeviceId(string installId, int userId)
|
public KoreaderBookDtoBuilder WithDeviceId(string installId, int userId)
|
||||||
{
|
{
|
||||||
_dto.Device_id = installId;
|
using var sha256 = SHA256.Create();
|
||||||
|
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(installId + userId));
|
||||||
|
_dto.Device_id = hash.ToString();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using API.DTOs.Koreader;
|
||||||
using API.DTOs.Progress;
|
using API.DTOs.Progress;
|
||||||
using API.Helpers;
|
using API.Helpers;
|
||||||
using API.Helpers.Builders;
|
using API.Helpers.Builders;
|
||||||
|
using Kavita.Common;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services;
|
namespace API.Services;
|
||||||
|
|
@ -20,12 +21,14 @@ public class KoreaderService : IKoreaderService
|
||||||
{
|
{
|
||||||
private readonly IReaderService _readerService;
|
private readonly IReaderService _readerService;
|
||||||
private readonly IUnitOfWork _unitOfWork;
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
private readonly ILocalizationService _localizationService;
|
||||||
private readonly ILogger<KoreaderService> _logger;
|
private readonly ILogger<KoreaderService> _logger;
|
||||||
|
|
||||||
public KoreaderService(IReaderService readerService, IUnitOfWork unitOfWork, ILogger<KoreaderService> logger)
|
public KoreaderService(IReaderService readerService, IUnitOfWork unitOfWork, ILocalizationService localizationService, ILogger<KoreaderService> logger)
|
||||||
{
|
{
|
||||||
_readerService = readerService;
|
_readerService = readerService;
|
||||||
_unitOfWork = unitOfWork;
|
_unitOfWork = unitOfWork;
|
||||||
|
_localizationService = localizationService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,10 +46,17 @@ public class KoreaderService : IKoreaderService
|
||||||
var userProgressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
|
var userProgressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
|
||||||
if (userProgressDto == null)
|
if (userProgressDto == null)
|
||||||
{
|
{
|
||||||
// TODO: Handle this case
|
var chapterDto = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(file.ChapterId);
|
||||||
|
if (chapterDto == null) return;
|
||||||
|
|
||||||
|
var volumeDto = await _unitOfWork.VolumeRepository.GetVolumeByIdAsync(chapterDto.VolumeId);
|
||||||
|
if (volumeDto == null) return;
|
||||||
|
|
||||||
userProgressDto = new ProgressDto()
|
userProgressDto = new ProgressDto()
|
||||||
{
|
{
|
||||||
ChapterId = file.ChapterId,
|
ChapterId = file.ChapterId,
|
||||||
|
VolumeId = chapterDto.VolumeId,
|
||||||
|
SeriesId = volumeDto.SeriesId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Update the bookScrollId if possible
|
// Update the bookScrollId if possible
|
||||||
|
|
@ -68,15 +78,14 @@ public class KoreaderService : IKoreaderService
|
||||||
|
|
||||||
var file = await _unitOfWork.MangaFileRepository.GetByKoreaderHash(bookHash);
|
var file = await _unitOfWork.MangaFileRepository.GetByKoreaderHash(bookHash);
|
||||||
|
|
||||||
// TODO: How do we handle when file isn't found by hash?
|
if (file == null) throw new KavitaException(await _localizationService.Translate(userId, "file-missing"));
|
||||||
if (file == null) return builder.Build();
|
|
||||||
|
|
||||||
var progressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
|
var progressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
|
||||||
var koreaderProgress = KoreaderHelper.GetKoreaderPosition(progressDto);
|
var koreaderProgress = KoreaderHelper.GetKoreaderPosition(progressDto);
|
||||||
|
|
||||||
return builder.WithProgress(koreaderProgress)
|
return builder.WithProgress(koreaderProgress)
|
||||||
.WithPercentage(progressDto?.PageNum, file.Pages)
|
.WithPercentage(progressDto?.PageNum, file.Pages)
|
||||||
.WithDeviceId(settingsDto.InstallId, userId) // TODO: Should we generate a hash for UserId + InstallId so that this DeviceId is unique to the user on the server?
|
.WithDeviceId(settingsDto.InstallId, userId)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
// const IP = 'localhost';
|
// const IP = 'localhost';
|
||||||
const IP = '10.10.30.215';
|
const IP = 'localhost';
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue