diff --git a/API.Benchmark/API.Benchmark.csproj b/API.Benchmark/API.Benchmark.csproj new file mode 100644 index 000000000..529f3cc93 --- /dev/null +++ b/API.Benchmark/API.Benchmark.csproj @@ -0,0 +1,18 @@ + + + + net5.0 + Exe + + + + + + + + + + + + + diff --git a/API.Benchmark/ParseScannedFilesBenchmarks.cs b/API.Benchmark/ParseScannedFilesBenchmarks.cs new file mode 100644 index 000000000..bc0c810ce --- /dev/null +++ b/API.Benchmark/ParseScannedFilesBenchmarks.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using API.Entities.Enums; +using API.Interfaces.Services; +using API.Services; +using API.Services.Tasks.Scanner; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Order; +using Microsoft.Extensions.Logging; +using NSubstitute; + +namespace API.Benchmark +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.FastestToSlowest)] + [RankColumn] + [SimpleJob(launchCount: 1, warmupCount: 3, targetCount: 5, invocationCount: 100, id: "Test"), ShortRunJob] + public class ParseScannedFilesBenchmarks + { + private readonly ParseScannedFiles _parseScannedFiles; + private readonly ILogger _logger = Substitute.For>(); + private readonly ILogger _bookLogger = Substitute.For>(); + + public ParseScannedFilesBenchmarks() + { + IBookService bookService = new BookService(_bookLogger); + _parseScannedFiles = new ParseScannedFiles(bookService, _logger); + } + + [Benchmark] + public void Test() + { + var libraryPath = Path.Join(Directory.GetCurrentDirectory(), + "../../../Services/Test Data/ScannerService/Manga"); + var parsedSeries = _parseScannedFiles.ScanLibrariesForSeries(LibraryType.Manga, new string[] {libraryPath}, + out var totalFiles, out var scanElapsedTime); + } + + } +} diff --git a/API.Benchmark/Program.cs b/API.Benchmark/Program.cs new file mode 100644 index 000000000..8111eaecc --- /dev/null +++ b/API.Benchmark/Program.cs @@ -0,0 +1,18 @@ +using BenchmarkDotNet.Running; + +namespace API.Benchmark +{ + /// + /// To build this, cd into API.Benchmark directory and run + /// dotnet build -c Release + /// then copy the outputted dll + /// dotnet copied_string\API.Benchmark.dll + /// + public class Program + { + static void Main(string[] args) + { + BenchmarkRunner.Run(); + } + } +} diff --git a/API.Tests/Parser/MangaParserTests.cs b/API.Tests/Parser/MangaParserTests.cs index 348902f84..fdd8cafa3 100644 --- a/API.Tests/Parser/MangaParserTests.cs +++ b/API.Tests/Parser/MangaParserTests.cs @@ -312,14 +312,14 @@ namespace API.Tests.Parser const string rootPath = @"E:/Manga/"; var expected = new Dictionary(); var filepath = @"E:/Manga/Mujaki no Rakuen/Mujaki no Rakuen Vol12 ch76.cbz"; - expected.Add(filepath, new ParserInfo - { - Series = "Mujaki no Rakuen", Volumes = "12", - Chapters = "76", Filename = "Mujaki no Rakuen Vol12 ch76.cbz", Format = MangaFormat.Archive, - FullFilePath = filepath - }); + expected.Add(filepath, new ParserInfo + { + Series = "Mujaki no Rakuen", Volumes = "12", + Chapters = "76", Filename = "Mujaki no Rakuen Vol12 ch76.cbz", Format = MangaFormat.Archive, + FullFilePath = filepath + }); - filepath = @"E:/Manga/Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/Vol 1.cbz"; + filepath = @"E:/Manga/Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen/Vol 1.cbz"; expected.Add(filepath, new ParserInfo { Series = "Shimoneta to Iu Gainen ga Sonzai Shinai Taikutsu na Sekai Man-hen", Volumes = "1", @@ -423,20 +423,20 @@ namespace API.Tests.Parser } Assert.NotNull(actual); _testOutputHelper.WriteLine($"Validating {file}"); - _testOutputHelper.WriteLine("Format"); Assert.Equal(expectedInfo.Format, actual.Format); - _testOutputHelper.WriteLine("Series"); + _testOutputHelper.WriteLine("Format ✓"); Assert.Equal(expectedInfo.Series, actual.Series); - _testOutputHelper.WriteLine("Chapters"); + _testOutputHelper.WriteLine("Series ✓"); Assert.Equal(expectedInfo.Chapters, actual.Chapters); - _testOutputHelper.WriteLine("Volumes"); + _testOutputHelper.WriteLine("Chapters ✓"); Assert.Equal(expectedInfo.Volumes, actual.Volumes); - _testOutputHelper.WriteLine("Edition"); + _testOutputHelper.WriteLine("Volumes ✓"); Assert.Equal(expectedInfo.Edition, actual.Edition); - _testOutputHelper.WriteLine("Filename"); + _testOutputHelper.WriteLine("Edition ✓"); Assert.Equal(expectedInfo.Filename, actual.Filename); - _testOutputHelper.WriteLine("FullFilePath"); + _testOutputHelper.WriteLine("Filename ✓"); Assert.Equal(expectedInfo.FullFilePath, actual.FullFilePath); + _testOutputHelper.WriteLine("FullFilePath ✓"); } } } diff --git a/API/Interfaces/ITaskScheduler.cs b/API/Interfaces/ITaskScheduler.cs index b75282fd5..ead76e36a 100644 --- a/API/Interfaces/ITaskScheduler.cs +++ b/API/Interfaces/ITaskScheduler.cs @@ -1,4 +1,6 @@ -namespace API.Interfaces +using System.Threading.Tasks; + +namespace API.Interfaces { public interface ITaskScheduler { @@ -6,7 +8,7 @@ /// For use on Server startup /// void ScheduleTasks(); - void ScheduleStatsTasks(); + Task ScheduleStatsTasks(); void ScheduleUpdaterTasks(); void ScanLibrary(int libraryId, bool forceUpdate = false); void CleanupChapters(int[] chapterIds); @@ -15,5 +17,6 @@ void RefreshSeriesMetadata(int libraryId, int seriesId); void ScanSeries(int libraryId, int seriesId, bool forceUpdate = false); void CancelStatsTasks(); + void RunStatCollection(); } } diff --git a/API/Interfaces/Services/IStatsService.cs b/API/Interfaces/Services/IStatsService.cs index f30222385..19d8d9f4b 100644 --- a/API/Interfaces/Services/IStatsService.cs +++ b/API/Interfaces/Services/IStatsService.cs @@ -6,8 +6,6 @@ namespace API.Interfaces.Services public interface IStatsService { Task PathData(ClientInfoDto clientInfoDto); - Task FinalizeStats(); - Task CollectRelevantData(); Task CollectAndSendStatsData(); } } diff --git a/API/Services/HostedServices/StartupTasksHostedService.cs b/API/Services/HostedServices/StartupTasksHostedService.cs index 02e2fa3d9..58b6eec25 100644 --- a/API/Services/HostedServices/StartupTasksHostedService.cs +++ b/API/Services/HostedServices/StartupTasksHostedService.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; using API.Interfaces; -using API.Interfaces.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -27,7 +26,10 @@ namespace API.Services.HostedServices try { - await ManageStartupStatsTasks(scope, taskScheduler); + // These methods will automatically check if stat collection is disabled to prevent sending any data regardless + // of when setting was changed + await taskScheduler.ScheduleStatsTasks(); + taskScheduler.RunStatCollection(); } catch (Exception) { @@ -35,21 +37,6 @@ namespace API.Services.HostedServices } } - private async Task ManageStartupStatsTasks(IServiceScope serviceScope, ITaskScheduler taskScheduler) - { - var unitOfWork = serviceScope.ServiceProvider.GetRequiredService(); - - var settingsDto = await unitOfWork.SettingsRepository.GetSettingsDtoAsync(); - - if (!settingsDto.AllowStatCollection) return; - - taskScheduler.ScheduleStatsTasks(); - - var statsService = serviceScope.ServiceProvider.GetRequiredService(); - - await statsService.CollectAndSendStatsData(); - } - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } } diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index d3f61e545..f12926c68 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -22,6 +22,7 @@ namespace API.Services private readonly IStatsService _statsService; private readonly IVersionUpdaterService _versionUpdaterService; + private const string SendDataTask = "finalize-stats"; public static BackgroundJobServer Client => new BackgroundJobServer(); @@ -76,19 +77,17 @@ namespace API.Services #region StatsTasks - private const string SendDataTask = "finalize-stats"; - public void ScheduleStatsTasks() + + public async Task ScheduleStatsTasks() { - var allowStatCollection = bool.Parse(Task.Run(() => _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.AllowStatCollection)).GetAwaiter().GetResult().Value); + var allowStatCollection = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).AllowStatCollection; if (!allowStatCollection) { _logger.LogDebug("User has opted out of stat collection, not registering tasks"); return; } - _logger.LogDebug("Adding StatsTasks"); - - _logger.LogDebug("Scheduling Send data to the Stats server {Setting}", nameof(Cron.Daily)); + _logger.LogDebug("Scheduling stat collection daily"); RecurringJob.AddOrUpdate(SendDataTask, () => _statsService.CollectAndSendStatsData(), Cron.Daily); } @@ -99,6 +98,12 @@ namespace API.Services RecurringJob.RemoveIfExists(SendDataTask); } + public void RunStatCollection() + { + _logger.LogInformation("Enqueuing stat collection"); + BackgroundJob.Enqueue(() => _statsService.CollectAndSendStatsData()); + } + #endregion #region UpdateTasks diff --git a/API/Services/Tasks/StatsService.cs b/API/Services/Tasks/StatsService.cs index 691d13f33..47087fc8d 100644 --- a/API/Services/Tasks/StatsService.cs +++ b/API/Services/Tasks/StatsService.cs @@ -50,7 +50,7 @@ namespace API.Services.Tasks await SaveFile(statisticsDto); } - public async Task CollectRelevantData() + private async Task CollectRelevantData() { _logger.LogDebug("Collecting data from the server and database"); @@ -63,7 +63,7 @@ namespace API.Services.Tasks await PathData(serverInfo, usageInfo); } - public async Task FinalizeStats() + private async Task FinalizeStats() { try { @@ -86,6 +86,12 @@ namespace API.Services.Tasks public async Task CollectAndSendStatsData() { + var allowStatCollection = (await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).AllowStatCollection; + if (!allowStatCollection) + { + _logger.LogDebug("User has opted out of stat collection, not registering tasks"); + return; + } await CollectRelevantData(); await FinalizeStats(); } diff --git a/Kavita.Common/HashUtil.cs b/Kavita.Common/HashUtil.cs index f0098f8eb..f959f0af4 100644 --- a/Kavita.Common/HashUtil.cs +++ b/Kavita.Common/HashUtil.cs @@ -1,5 +1,4 @@ using System; -using System.Security.Cryptography; using System.Text; namespace Kavita.Common diff --git a/Kavita.sln b/Kavita.sln index e49484b02..670808870 100644 --- a/Kavita.sln +++ b/Kavita.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Tests", "API.Tests\API. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kavita.Common", "Kavita.Common\Kavita.Common.csproj", "{165A86F5-9E74-4C05-9305-A6F0BA32C9EE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API.Benchmark", "API.Benchmark\API.Benchmark.csproj", "{3D781D18-2452-421F-A81A-59254FEE1FEC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -58,5 +60,17 @@ Global {165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x64.Build.0 = Release|Any CPU {165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x86.ActiveCfg = Release|Any CPU {165A86F5-9E74-4C05-9305-A6F0BA32C9EE}.Release|x86.Build.0 = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|x64.Build.0 = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Debug|x86.Build.0 = Debug|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|Any CPU.Build.0 = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|x64.ActiveCfg = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|x64.Build.0 = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|x86.ActiveCfg = Release|Any CPU + {3D781D18-2452-421F-A81A-59254FEE1FEC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts b/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts index 653fc21fe..ce3a8452b 100644 --- a/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts +++ b/UI/Web/src/app/user-settings/user-preferences/user-preferences.component.ts @@ -187,13 +187,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy { } transformKeyToOpdsUrl(key: string) { - let apiUrl = environment.apiUrl; - if (environment.production) { - apiUrl = `${location.protocol}//${location.origin}`; - if (location.port != '80') { - apiUrl += ':' + location.port; - } - } - return `${apiUrl}opds/${key}`; + return `${location.origin}/api/opds/${key}`; } }