mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Hangfire now dies gracefully when using CTRL+C rather than Stop button in Rider. Implemented one stream method for testing. Regenerated a few migrations due to oversight in index not taking account of library.
This commit is contained in:
parent
126fb57f4d
commit
9035b6cc4e
@ -126,5 +126,11 @@ namespace API.Controllers
|
|||||||
|
|
||||||
return BadRequest("There was an error with updating the series");
|
return BadRequest("There was an error with updating the series");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("recently-added")]
|
||||||
|
public async Task<ActionResult<IEnumerable<SeriesDto>>> GetRecentlyAdded(int libraryId = 0)
|
||||||
|
{
|
||||||
|
return Ok(await _unitOfWork.SeriesRepository.GetRecentlyAdded(libraryId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -112,5 +113,7 @@ namespace API.Data
|
|||||||
.Include(l => l.Folders)
|
.Include(l => l.Folders)
|
||||||
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider).ToListAsync();
|
.ProjectTo<LibraryDto>(_mapper.ConfigurationProvider).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,23 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
namespace API.Data.Migrations
|
|
||||||
{
|
|
||||||
public partial class SearchIndex : Migration
|
|
||||||
{
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_Series_Name_NormalizedName_LocalizedName",
|
|
||||||
table: "Series",
|
|
||||||
columns: new[] { "Name", "NormalizedName", "LocalizedName" },
|
|
||||||
unique: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropIndex(
|
|
||||||
name: "IX_Series_Name_NormalizedName_LocalizedName",
|
|
||||||
table: "Series");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,8 +9,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
namespace API.Data.Migrations
|
namespace API.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(DataContext))]
|
[DbContext(typeof(DataContext))]
|
||||||
[Migration("20210313001830_SearchIndex")]
|
[Migration("20210315134028_SearchIndexAndProgressDates")]
|
||||||
partial class SearchIndex
|
partial class SearchIndexAndProgressDates
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@ -161,6 +161,12 @@ namespace API.Data.Migrations
|
|||||||
b.Property<int>("ChapterId")
|
b.Property<int>("ChapterId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("PagesRead")
|
b.Property<int>("PagesRead")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -367,7 +373,7 @@ namespace API.Data.Migrations
|
|||||||
|
|
||||||
b.HasIndex("LibraryId");
|
b.HasIndex("LibraryId");
|
||||||
|
|
||||||
b.HasIndex("Name", "NormalizedName", "LocalizedName")
|
b.HasIndex("Name", "NormalizedName", "LocalizedName", "LibraryId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("Series");
|
b.ToTable("Series");
|
@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace API.Data.Migrations
|
||||||
|
{
|
||||||
|
public partial class SearchIndexAndProgressDates : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "Created",
|
||||||
|
table: "AppUserProgresses",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "LastModified",
|
||||||
|
table: "AppUserProgresses",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Series_Name_NormalizedName_LocalizedName_LibraryId",
|
||||||
|
table: "Series",
|
||||||
|
columns: new[] { "Name", "NormalizedName", "LocalizedName", "LibraryId" },
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "IX_Series_Name_NormalizedName_LocalizedName_LibraryId",
|
||||||
|
table: "Series");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Created",
|
||||||
|
table: "AppUserProgresses");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "LastModified",
|
||||||
|
table: "AppUserProgresses");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -159,6 +159,12 @@ namespace API.Data.Migrations
|
|||||||
b.Property<int>("ChapterId")
|
b.Property<int>("ChapterId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int>("PagesRead")
|
b.Property<int>("PagesRead")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
@ -365,7 +371,7 @@ namespace API.Data.Migrations
|
|||||||
|
|
||||||
b.HasIndex("LibraryId");
|
b.HasIndex("LibraryId");
|
||||||
|
|
||||||
b.HasIndex("Name", "NormalizedName", "LocalizedName")
|
b.HasIndex("Name", "NormalizedName", "LocalizedName", "LibraryId")
|
||||||
.IsUnique();
|
.IsUnique();
|
||||||
|
|
||||||
b.ToTable("Series");
|
b.ToTable("Series");
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -275,5 +277,54 @@ namespace API.Data
|
|||||||
v.PagesRead = userProgress.Where(p => p.VolumeId == v.Id).Sum(p => p.PagesRead);
|
v.PagesRead = userProgress.Where(p => p.VolumeId == v.Id).Sum(p => p.PagesRead);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of Series that were added within 2 weeks.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="libraryId">Library to restrict to, if 0, will apply to all libraries</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<IEnumerable<SeriesDto>> GetRecentlyAdded(int libraryId)
|
||||||
|
{
|
||||||
|
// && (libraryId <= 0 || s.LibraryId == libraryId)
|
||||||
|
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)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<SeriesDto>> GetSeriesStream(int userId)
|
||||||
|
{
|
||||||
|
// Testing out In Progress to figure out how to design generalized solution
|
||||||
|
var userProgress = await _context.AppUserProgresses
|
||||||
|
.Where(p => p.AppUserId == userId && p.PagesRead > 0)
|
||||||
|
.AsNoTracking()
|
||||||
|
.ToListAsync();
|
||||||
|
if (!userProgress.Any()) return new SeriesDto[] {};
|
||||||
|
|
||||||
|
var seriesIds = userProgress.Select(p => p.SeriesId).ToList();
|
||||||
|
/*
|
||||||
|
*select P.*, S.Name, S.Pages from AppUserProgresses AS P
|
||||||
|
LEFT join Series as "S" on s.Id = P.SeriesId
|
||||||
|
where AppUserId = 1 AND P.PagesRead > 0 AND P.PagesRead < S.Pages
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// var series = await _context.Series
|
||||||
|
// .Where(s => seriesIds.Contains(s.Id) && s.Pages) // I need a join
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return new SeriesDto[] {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,20 +1,25 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using API.Entities.Interfaces;
|
||||||
|
|
||||||
namespace API.Entities
|
namespace API.Entities
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the progress a single user has on a given Volume. Progress is realistically tracked against the Volume's chapters.
|
/// Represents the progress a single user has on a given Volume. Progress is realistically tracked against the Volume's chapters.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppUserProgress
|
public class AppUserProgress : IEntityDate
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
public int PagesRead { get; set; }
|
public int PagesRead { get; set; }
|
||||||
public int VolumeId { get; set; }
|
public int VolumeId { get; set; }
|
||||||
public int SeriesId { get; set; }
|
public int SeriesId { get; set; }
|
||||||
|
|
||||||
public int ChapterId { get; set; }
|
public int ChapterId { get; set; }
|
||||||
|
|
||||||
// Relationships
|
// Relationships
|
||||||
public AppUser AppUser { get; set; }
|
public AppUser AppUser { get; set; }
|
||||||
public int AppUserId { get; set; }
|
public int AppUserId { get; set; }
|
||||||
|
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
public DateTime LastModified { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace API.Entities
|
namespace API.Entities
|
||||||
{
|
{
|
||||||
[Index(nameof(Name), nameof(NormalizedName), nameof(LocalizedName), IsUnique = true)]
|
[Index(nameof(Name), nameof(NormalizedName), nameof(LocalizedName), nameof(LibraryId), IsUnique = true)]
|
||||||
public class Series : IEntityDate
|
public class Series : IEntityDate
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
@ -36,7 +36,6 @@ namespace API.Entities
|
|||||||
public DateTime Created { get; set; }
|
public DateTime Created { get; set; }
|
||||||
public DateTime LastModified { get; set; }
|
public DateTime LastModified { get; set; }
|
||||||
public byte[] CoverImage { get; set; }
|
public byte[] CoverImage { get; set; }
|
||||||
// NOTE: Do I want to store a thumbImage for search results?
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sum of all Volume page counts
|
/// Sum of all Volume page counts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -55,5 +55,6 @@ namespace API.Interfaces
|
|||||||
|
|
||||||
Task<byte[]> GetVolumeCoverImageAsync(int volumeId);
|
Task<byte[]> GetVolumeCoverImageAsync(int volumeId);
|
||||||
Task<byte[]> GetSeriesCoverImageAsync(int seriesId);
|
Task<byte[]> GetSeriesCoverImageAsync(int seriesId);
|
||||||
|
Task<IEnumerable<SeriesDto>> GetRecentlyAdded(int libraryId);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -41,7 +41,6 @@ namespace API
|
|||||||
logger.LogError(ex, "An error occurred during migration");
|
logger.LogError(ex, "An error occurred during migration");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ namespace API.Services
|
|||||||
return chapter;
|
return chapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Performing cleanup of Cache directory");
|
_logger.LogInformation("Performing cleanup of Cache directory");
|
||||||
|
@ -21,7 +21,7 @@ namespace API.Services
|
|||||||
private readonly ICleanupService _cleanupService;
|
private readonly ICleanupService _cleanupService;
|
||||||
private readonly IDirectoryService _directoryService;
|
private readonly IDirectoryService _directoryService;
|
||||||
|
|
||||||
public BackgroundJobServer Client => new BackgroundJobServer();
|
public static BackgroundJobServer Client => new BackgroundJobServer();
|
||||||
// new BackgroundJobServerOptions()
|
// new BackgroundJobServerOptions()
|
||||||
// {
|
// {
|
||||||
// WorkerCount = 1
|
// WorkerCount = 1
|
||||||
@ -40,15 +40,13 @@ namespace API.Services
|
|||||||
_cleanupService = cleanupService;
|
_cleanupService = cleanupService;
|
||||||
_directoryService = directoryService;
|
_directoryService = directoryService;
|
||||||
|
|
||||||
//Hangfire.RecurringJob.RemoveIfExists();
|
|
||||||
ScheduleTasks();
|
ScheduleTasks();
|
||||||
//JobStorage.Current.GetMonitoringApi().EnqueuedJobs()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScheduleTasks()
|
public void ScheduleTasks()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Scheduling reoccurring tasks");
|
_logger.LogInformation("Scheduling reoccurring tasks");
|
||||||
|
|
||||||
string setting = null;
|
string setting = null;
|
||||||
setting = Task.Run(() => _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskScan)).Result.Value;
|
setting = Task.Run(() => _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskScan)).Result.Value;
|
||||||
if (setting != null)
|
if (setting != null)
|
||||||
|
@ -3,11 +3,13 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using API.Entities.Enums;
|
using API.Entities.Enums;
|
||||||
using API.Extensions;
|
using API.Extensions;
|
||||||
using API.Interfaces;
|
using API.Interfaces;
|
||||||
using API.Interfaces.Services;
|
using API.Interfaces.Services;
|
||||||
|
using Hangfire;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@ -57,6 +59,7 @@ namespace API.Services.Tasks
|
|||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutomaticRetry(Attempts = 3, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Fail)]
|
||||||
public void BackupDatabase()
|
public void BackupDatabase()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Beginning backup of Database at {BackupTime}", DateTime.Now);
|
_logger.LogInformation("Beginning backup of Database at {BackupTime}", DateTime.Now);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using API.Interfaces.Services;
|
using API.Interfaces.Services;
|
||||||
|
using Hangfire;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace API.Services.Tasks
|
namespace API.Services.Tasks
|
||||||
@ -20,6 +21,7 @@ namespace API.Services.Tasks
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutomaticRetry(Attempts = 3, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Fail)]
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Cleaning temp directory");
|
_logger.LogInformation("Cleaning temp directory");
|
||||||
|
@ -33,7 +33,8 @@ namespace API.Services.Tasks
|
|||||||
_metadataService = metadataService;
|
_metadataService = metadataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DisableConcurrentExecution(timeoutInSeconds: 120)]
|
[DisableConcurrentExecution(timeoutInSeconds: 5)]
|
||||||
|
[AutomaticRetry(Attempts = 0, LogEvents = false, OnAttemptsExceeded = AttemptsExceededAction.Delete)]
|
||||||
public void ScanLibraries()
|
public void ScanLibraries()
|
||||||
{
|
{
|
||||||
var libraries = Task.Run(() => _unitOfWork.LibraryRepository.GetLibrariesAsync()).Result.ToList();
|
var libraries = Task.Run(() => _unitOfWork.LibraryRepository.GetLibrariesAsync()).Result.ToList();
|
||||||
|
@ -18,6 +18,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
|
|||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
using IApplicationLifetime = Microsoft.AspNetCore.Hosting.IApplicationLifetime;
|
||||||
|
|
||||||
namespace API
|
namespace API
|
||||||
{
|
{
|
||||||
@ -70,7 +71,7 @@ namespace API
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env)
|
public void Configure(IApplicationBuilder app, IBackgroundJobClient backgroundJobs, IWebHostEnvironment env, IHostApplicationLifetime applicationLifetime)
|
||||||
{
|
{
|
||||||
app.UseMiddleware<ExceptionMiddleware>();
|
app.UseMiddleware<ExceptionMiddleware>();
|
||||||
|
|
||||||
@ -125,6 +126,16 @@ namespace API
|
|||||||
endpoints.MapHangfireDashboard();
|
endpoints.MapHangfireDashboard();
|
||||||
endpoints.MapFallbackToController("Index", "Fallback");
|
endpoints.MapFallbackToController("Index", "Fallback");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
applicationLifetime.ApplicationStopping.Register(OnShutdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShutdown()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Server is shutting down. Going to dispose Hangfire");
|
||||||
|
//this code is called when the application stops
|
||||||
|
//TaskScheduler.Client.Dispose();
|
||||||
|
System.Threading.Thread.Sleep(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user