Added API benchmark, hash device_id, handled null cases in GET/PUT

This commit is contained in:
Tyler Kenney 2025-05-20 09:35:39 -04:00
parent e84aed357a
commit f30e3b17d6
7 changed files with 79 additions and 11 deletions

View file

@ -26,5 +26,10 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<None Update="Data\AesopsFables.epub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View 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");
}
}
}
}

View file

@ -77,6 +77,8 @@ public class KoreaderController : BaseApiController
[HttpGet("{apiKey}/syncs/progress/{ebookHash}")]
public async Task<ActionResult<KoreaderBookDto>> GetProgress(string apiKey, string ebookHash)
{
try
{
var userId = await GetUserId(apiKey);
var response = await _koreaderService.GetProgress(ebookHash, userId);
@ -84,6 +86,13 @@ public class KoreaderController : BaseApiController
return Ok(response);
}
catch (KavitaException ex)
{
return BadRequest(ex.Message);
}
return BadRequest();
}
private async Task<int> GetUserId(string apiKey)
{

View file

@ -1,3 +1,5 @@
using System.Security.Cryptography;
using System.Text;
using API.DTOs.Koreader;
namespace API.Helpers.Builders;
@ -36,7 +38,9 @@ public class KoreaderBookDtoBuilder : IEntityBuilder<KoreaderBookDto>
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;
}
}

View file

@ -4,6 +4,7 @@ using API.DTOs.Koreader;
using API.DTOs.Progress;
using API.Helpers;
using API.Helpers.Builders;
using Kavita.Common;
using Microsoft.Extensions.Logging;
namespace API.Services;
@ -20,12 +21,14 @@ public class KoreaderService : IKoreaderService
{
private readonly IReaderService _readerService;
private readonly IUnitOfWork _unitOfWork;
private readonly ILocalizationService _localizationService;
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;
_unitOfWork = unitOfWork;
_localizationService = localizationService;
_logger = logger;
}
@ -43,10 +46,17 @@ public class KoreaderService : IKoreaderService
var userProgressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
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()
{
ChapterId = file.ChapterId,
VolumeId = chapterDto.VolumeId,
SeriesId = volumeDto.SeriesId,
};
}
// Update the bookScrollId if possible
@ -68,15 +78,14 @@ public class KoreaderService : IKoreaderService
var file = await _unitOfWork.MangaFileRepository.GetByKoreaderHash(bookHash);
// TODO: How do we handle when file isn't found by hash?
if (file == null) return builder.Build();
if (file == null) throw new KavitaException(await _localizationService.Translate(userId, "file-missing"));
var progressDto = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(file.ChapterId, userId);
var koreaderProgress = KoreaderHelper.GetKoreaderPosition(progressDto);
return builder.WithProgress(koreaderProgress)
.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();
}
}

View file

@ -3,7 +3,7 @@
// The list of file replacements can be found in `angular.json`.
// const IP = 'localhost';
const IP = '10.10.30.215';
const IP = 'localhost';
export const environment = {
production: false,