diff --git a/API/Controllers/SeriesController.cs b/API/Controllers/SeriesController.cs index d4bd4c5a9..611fff59b 100644 --- a/API/Controllers/SeriesController.cs +++ b/API/Controllers/SeriesController.cs @@ -128,9 +128,18 @@ namespace API.Controllers } [HttpGet("recently-added")] - public async Task>> GetRecentlyAdded(int libraryId = 0) + public async Task>> GetRecentlyAdded(int libraryId = 0, int limit = 20) { - return Ok(await _unitOfWork.SeriesRepository.GetRecentlyAdded(libraryId)); + return Ok(await _unitOfWork.SeriesRepository.GetRecentlyAdded(libraryId, limit)); } + + [HttpGet("in-progress")] + public async Task>> GetInProgress(int libraryId = 0, int limit = 20) + { + var user = await _unitOfWork.UserRepository.GetUserByUsernameAsync(User.GetUsername()); + return Ok(await _unitOfWork.SeriesRepository.GetInProgress(user.Id, libraryId, limit)); + } + + } } \ No newline at end of file diff --git a/API/Data/SeriesRepository.cs b/API/Data/SeriesRepository.cs index e4bda9997..29e3aaadb 100644 --- a/API/Data/SeriesRepository.cs +++ b/API/Data/SeriesRepository.cs @@ -11,6 +11,7 @@ using API.Interfaces; using AutoMapper; using AutoMapper.QueryableExtensions; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; namespace API.Data @@ -282,20 +283,55 @@ namespace API.Data /// Returns a list of Series that were added within 2 weeks. /// /// Library to restrict to, if 0, will apply to all libraries + /// /// - public async Task> GetRecentlyAdded(int libraryId) + public async Task> GetRecentlyAdded(int libraryId, int limit) { - // && (libraryId <= 0 || s.LibraryId == libraryId) + // TODO: Remove 2 week condition var twoWeeksAgo = DateTime.Today.Subtract(TimeSpan.FromDays(14)); _logger.LogDebug("2 weeks from today is: {Date}", twoWeeksAgo); return await _context.Series - .Where(s => s.Created > twoWeeksAgo) - .Take(20) + .Where(s => s.Created > twoWeeksAgo && (libraryId <= 0 || s.LibraryId == libraryId)) + .Take(limit) + .OrderBy(s => s.Created) .AsNoTracking() .ProjectTo(_mapper.ConfigurationProvider) .ToListAsync(); } + + public async Task> GetInProgress(int userId, int libraryId, int limit) + { + //&& (libraryId <= 0 || s.Series.LibraryId == libraryId) + var twoWeeksAgo = DateTime.Today.Subtract(TimeSpan.FromDays(14)); + _logger.LogInformation("GetInProgress"); + _logger.LogDebug("2 weeks from today is: {Date}", twoWeeksAgo); + // var series = await _context.Series + // .Join(_context.AppUserProgresses, s => s.Id, progress => progress.SeriesId, (s, progress) => new + // { + // Series = s, + // Progress = progress + // }) + // .DefaultIfEmpty() + // .Where(s => s.Series.Created > twoWeeksAgo + // && s.Progress.AppUserId == userId + // && s.Progress.PagesRead > s.Series.Pages) + // .Take(limit) + // .OrderBy(s => s.Series.Created) + // .AsNoTracking() + // .Select(s => s.Series) + // .ProjectTo(_mapper.ConfigurationProvider) + // .ToListAsync(); + var series = await _context.Series + .Where(s => s.Created > twoWeeksAgo) // && (libraryId <= 0 || s.LibraryId == libraryId) + .AsNoTracking() + .ProjectTo(_mapper.ConfigurationProvider) + .ToListAsync(); + + await AddSeriesModifiers(userId, series); + + return series.Where(s => s.PagesRead > 0).Take(limit).ToList(); + } public async Task> GetSeriesStream(int userId) diff --git a/API/Extensions/ServiceCollectionExtensions.cs b/API/Extensions/ServiceCollectionExtensions.cs index aa4c6f0f7..d3cae4191 100644 --- a/API/Extensions/ServiceCollectionExtensions.cs +++ b/API/Extensions/ServiceCollectionExtensions.cs @@ -1,7 +1,12 @@ -namespace API.Extensions +using API.Interfaces.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace API.Extensions { - public class ServiceCollectionExtensions + public static class ServiceCollectionExtensions { - + public static IServiceCollection AddStartupTask(this IServiceCollection services) + where T : class, IStartupTask + => services.AddTransient(); } } \ No newline at end of file diff --git a/API/Interfaces/ISeriesRepository.cs b/API/Interfaces/ISeriesRepository.cs index 03cc758b1..647469e69 100644 --- a/API/Interfaces/ISeriesRepository.cs +++ b/API/Interfaces/ISeriesRepository.cs @@ -55,6 +55,7 @@ namespace API.Interfaces Task GetVolumeCoverImageAsync(int volumeId); Task GetSeriesCoverImageAsync(int seriesId); - Task> GetRecentlyAdded(int libraryId); + Task> GetInProgress(int userId, int libraryId, int limit); + Task> GetRecentlyAdded(int libraryId, int limit); } } \ No newline at end of file diff --git a/API/Middleware/ExceptionMiddleware.cs b/API/Middleware/ExceptionMiddleware.cs index 5c168cf3d..8c76b8644 100644 --- a/API/Middleware/ExceptionMiddleware.cs +++ b/API/Middleware/ExceptionMiddleware.cs @@ -25,6 +25,7 @@ namespace API.Middleware public async Task InvokeAsync(HttpContext context) { + // BUG: I think Hangfire timeouts are triggering the middleware to hijack an API call try { await _next(context); // downstream middlewares or http call diff --git a/API/Program.cs b/API/Program.cs index 7bd6ef289..c38736407 100644 --- a/API/Program.cs +++ b/API/Program.cs @@ -3,6 +3,8 @@ using System.Threading.Tasks; using API.Data; using API.Entities; using API.Interfaces; +using API.Interfaces.Services; +using API.Services; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -40,6 +42,15 @@ namespace API var logger = services.GetRequiredService < ILogger>(); logger.LogError(ex, "An error occurred during migration"); } + + // Load all tasks from DI (TODO: This is not working) + var startupTasks = host.Services.GetServices(); + + // Execute all the tasks + foreach (var startupTask in startupTasks) + { + await startupTask.ExecuteAsync(); + } await host.RunAsync(); } diff --git a/API/Services/TaskScheduler.cs b/API/Services/TaskScheduler.cs index 758d653a2..70bca9121 100644 --- a/API/Services/TaskScheduler.cs +++ b/API/Services/TaskScheduler.cs @@ -21,11 +21,11 @@ namespace API.Services private readonly ICleanupService _cleanupService; private readonly IDirectoryService _directoryService; - public static BackgroundJobServer Client => new BackgroundJobServer(); - // new BackgroundJobServerOptions() - // { - // WorkerCount = 1 - // } + public static BackgroundJobServer Client => new BackgroundJobServer(new BackgroundJobServerOptions() + { + WorkerCount = 1 + }); + public TaskScheduler(ICacheService cacheService, ILogger logger, IScannerService scannerService, IUnitOfWork unitOfWork, IMetadataService metadataService, IBackupService backupService, ICleanupService cleanupService, diff --git a/API/Services/Tasks/ScannerService.cs b/API/Services/Tasks/ScannerService.cs index af7b6eca8..61842eac8 100644 --- a/API/Services/Tasks/ScannerService.cs +++ b/API/Services/Tasks/ScannerService.cs @@ -33,7 +33,7 @@ namespace API.Services.Tasks _metadataService = metadataService; } - [DisableConcurrentExecution(timeoutInSeconds: 5)] + //[DisableConcurrentExecution(timeoutInSeconds: 5)] [AutomaticRetry(Attempts = 0, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Delete)] public void ScanLibraries() { @@ -64,7 +64,7 @@ namespace API.Services.Tasks _scannedSeries = null; } - [DisableConcurrentExecution(5)] + //[DisableConcurrentExecution(5)] [AutomaticRetry(Attempts = 0, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Delete)] public void ScanLibrary(int libraryId, bool forceUpdate) { diff --git a/API/Services/WarmupServiceStartupTask.cs b/API/Services/WarmupServiceStartupTask.cs index fd9b1745b..36463451a 100644 --- a/API/Services/WarmupServiceStartupTask.cs +++ b/API/Services/WarmupServiceStartupTask.cs @@ -18,14 +18,13 @@ namespace API.Services _provider = provider; } - public Task ExecuteAsync(CancellationToken cancellationToken) + public Task ExecuteAsync(CancellationToken cancellationToken = default) { - using (var scope = _provider.CreateScope()) + using var scope = _provider.CreateScope(); + foreach (var singleton in GetServices(_services)) { - foreach (var singleton in GetServices(_services)) - { - scope.ServiceProvider.GetServices(singleton); - } + Console.WriteLine("DI preloading of " + singleton.FullName); + scope.ServiceProvider.GetServices(singleton); } return Task.CompletedTask; diff --git a/API/Startup.cs b/API/Startup.cs index d53e346d6..55b7f6f9e 100644 --- a/API/Startup.cs +++ b/API/Startup.cs @@ -64,10 +64,9 @@ namespace API services.AddResponseCaching(); - services - .AddStartupTask() - .TryAddSingleton(services); - + // services + // .AddStartupTask() + // //.TryAddSingleton(services); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -134,7 +133,7 @@ namespace API { Console.WriteLine("Server is shutting down. Going to dispose Hangfire"); //this code is called when the application stops - //TaskScheduler.Client.Dispose(); + TaskScheduler.Client.Dispose(); System.Threading.Thread.Sleep(1000); } }