mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Ability to update settings. Take effect on next reboot.
This commit is contained in:
parent
e60f795410
commit
1050fa4e54
28
API.Tests/Converters/CronConverterTests.cs
Normal file
28
API.Tests/Converters/CronConverterTests.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using API.Helpers.Converters;
|
||||
using AutoMapper;
|
||||
using Hangfire;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace API.Tests.Converters
|
||||
{
|
||||
public class CronConverterTests
|
||||
{
|
||||
private readonly ITestOutputHelper _testOutputHelper;
|
||||
|
||||
public CronConverterTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_testOutputHelper = testOutputHelper;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("daily", "0 0 * * *")]
|
||||
[InlineData("disabled", "0 0 31 2 *")]
|
||||
[InlineData("weekly", "0 0 * * 1")]
|
||||
public void ConvertTest(string input, string expected)
|
||||
{
|
||||
Assert.Equal(expected, CronConverter.ConvertToCronNotation(input));
|
||||
}
|
||||
}
|
||||
}
|
@ -20,9 +20,5 @@ namespace API.Controllers
|
||||
var users = await _userManager.GetUsersInRoleAsync("Admin");
|
||||
return users.Count > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.DTOs;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Helpers.Converters;
|
||||
using API.Interfaces;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace API.Controllers
|
||||
@ -18,30 +18,30 @@ namespace API.Controllers
|
||||
{
|
||||
private readonly DataContext _dataContext;
|
||||
private readonly ILogger<SettingsController> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly ITaskScheduler _taskScheduler;
|
||||
|
||||
public SettingsController(DataContext dataContext, ILogger<SettingsController> logger, IMapper mapper, ITaskScheduler taskScheduler)
|
||||
public SettingsController(DataContext dataContext, ILogger<SettingsController> logger, IUnitOfWork unitOfWork,
|
||||
ITaskScheduler taskScheduler)
|
||||
{
|
||||
_dataContext = dataContext;
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
_unitOfWork = unitOfWork;
|
||||
_taskScheduler = taskScheduler;
|
||||
}
|
||||
|
||||
[HttpGet("")]
|
||||
public async Task<ActionResult<ServerSettingDto>> GetSettings()
|
||||
{
|
||||
var settings = await _dataContext.ServerSetting.Select(x => x).ToListAsync();
|
||||
return _mapper.Map<ServerSettingDto>(settings);
|
||||
return Ok(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync());
|
||||
}
|
||||
|
||||
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpPost("")]
|
||||
public async Task<ActionResult> UpdateSettings(ServerSettingDto updateSettingsDto)
|
||||
public async Task<ActionResult<ServerSettingDto>> UpdateSettings(ServerSettingDto updateSettingsDto)
|
||||
{
|
||||
_logger.LogInformation($"{User.GetUsername()} is updating Server Settings");
|
||||
|
||||
|
||||
if (updateSettingsDto.CacheDirectory.Equals(string.Empty))
|
||||
{
|
||||
return BadRequest("Cache Directory cannot be empty");
|
||||
@ -51,13 +51,39 @@ namespace API.Controllers
|
||||
{
|
||||
return BadRequest("Directory does not exist or is not accessible.");
|
||||
}
|
||||
// TODO: Figure out how to handle a change. This means that on clean, we need to clean up old cache
|
||||
// directory and new one, but what if someone is reading?
|
||||
// I can just clean both always, /cache/ is an owned folder, so users shouldn't use it.
|
||||
|
||||
|
||||
//_dataContext.ServerSetting.Update
|
||||
return BadRequest("Not Implemented");
|
||||
|
||||
// We do not allow CacheDirectory changes, so we will ignore.
|
||||
var currentSettings = await _unitOfWork.SettingsRepository.GetSettingsAsync();
|
||||
|
||||
foreach (var setting in currentSettings)
|
||||
{
|
||||
if (setting.Key == ServerSettingKey.TaskBackup && updateSettingsDto.TaskBackup != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.TaskBackup;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
|
||||
if (setting.Key == ServerSettingKey.TaskScan && updateSettingsDto.TaskScan != setting.Value)
|
||||
{
|
||||
setting.Value = updateSettingsDto.TaskScan;
|
||||
_unitOfWork.SettingsRepository.Update(setting);
|
||||
}
|
||||
}
|
||||
|
||||
if (_unitOfWork.HasChanges() && await _unitOfWork.Complete())
|
||||
{
|
||||
_logger.LogInformation("Server Settings updated.");
|
||||
return Ok(updateSettingsDto);
|
||||
}
|
||||
|
||||
return BadRequest("There was a critical issue. Please try again.");
|
||||
}
|
||||
|
||||
[Authorize(Policy = "RequireAdminRole")]
|
||||
[HttpGet("task-frequencies")]
|
||||
public ActionResult<IEnumerable<string>> GetTaskFrequencies()
|
||||
{
|
||||
return Ok(CronConverter.Options);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,8 @@
|
||||
public class ServerSettingDto
|
||||
{
|
||||
public string CacheDirectory { get; set; }
|
||||
// public string Kind { get; init; }
|
||||
// public string Value { get; init; }
|
||||
public string TaskScan { get; set; }
|
||||
public string LoggingLevel { get; set; }
|
||||
public string TaskBackup { get; set; }
|
||||
}
|
||||
}
|
676
API/Data/Migrations/20210203164258_ServerSettingsKey.Designer.cs
generated
Normal file
676
API/Data/Migrations/20210203164258_ServerSettingsKey.Designer.cs
generated
Normal file
@ -0,0 +1,676 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using API.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20210203164258_ServerSettingsKey")]
|
||||
partial class ServerSettingsKey
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "5.0.1");
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppRole", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUser", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastActive")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserProgress", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("PagesRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VolumeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.ToTable("AppUserProgresses");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserRating", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Rating")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Review")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.ToTable("AppUserRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserRole", b =>
|
||||
{
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("CoverImage")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Number")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Pages")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Range")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("VolumeId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("VolumeId");
|
||||
|
||||
b.ToTable("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.FolderPath", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastScanned")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("LibraryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Path")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LibraryId");
|
||||
|
||||
b.ToTable("FolderPath");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Library", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CoverImage")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ChapterId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FilePath")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Format")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("NumberOfPages")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ChapterId");
|
||||
|
||||
b.ToTable("MangaFile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Series", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("CoverImage")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("LibraryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OriginalName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Pages")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SortName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Summary")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("LibraryId");
|
||||
|
||||
b.ToTable("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.ServerSetting", b =>
|
||||
{
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("ServerSetting");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Volume", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<byte[]>("CoverImage")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSpecial")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("LastModified")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Number")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Pages")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SeriesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("SeriesId");
|
||||
|
||||
b.ToTable("Volume");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserLibrary", b =>
|
||||
{
|
||||
b.Property<int>("AppUsersId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LibrariesId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("AppUsersId", "LibrariesId");
|
||||
|
||||
b.HasIndex("LibrariesId");
|
||||
|
||||
b.ToTable("AppUserLibrary");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("RoleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
|
||||
{
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserProgress", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany("Progresses")
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserRating", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", "AppUser")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("AppUserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("AppUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUserRole", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppRole", "Role")
|
||||
.WithMany("UserRoles")
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.AppUser", "User")
|
||||
.WithMany("UserRoles")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Role");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Volume", "Volume")
|
||||
.WithMany("Chapters")
|
||||
.HasForeignKey("VolumeId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Volume");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.FolderPath", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Library", "Library")
|
||||
.WithMany("Folders")
|
||||
.HasForeignKey("LibraryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.MangaFile", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Chapter", "Chapter")
|
||||
.WithMany("Files")
|
||||
.HasForeignKey("ChapterId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Chapter");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Series", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Library", "Library")
|
||||
.WithMany("Series")
|
||||
.HasForeignKey("LibraryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Library");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Volume", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.Series", "Series")
|
||||
.WithMany("Volumes")
|
||||
.HasForeignKey("SeriesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("AppUserLibrary", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("AppUsersId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("API.Entities.Library", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("LibrariesId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<int>", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<int>", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<int>", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppRole", b =>
|
||||
{
|
||||
b.Navigation("UserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.AppUser", b =>
|
||||
{
|
||||
b.Navigation("Progresses");
|
||||
|
||||
b.Navigation("Ratings");
|
||||
|
||||
b.Navigation("UserRoles");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
{
|
||||
b.Navigation("Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Library", b =>
|
||||
{
|
||||
b.Navigation("Folders");
|
||||
|
||||
b.Navigation("Series");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Series", b =>
|
||||
{
|
||||
b.Navigation("Volumes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Volume", b =>
|
||||
{
|
||||
b.Navigation("Chapters");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
29
API/Data/Migrations/20210203164258_ServerSettingsKey.cs
Normal file
29
API/Data/Migrations/20210203164258_ServerSettingsKey.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
public partial class ServerSettingsKey : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Key",
|
||||
table: "ServerSetting",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "TEXT");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Key",
|
||||
table: "ServerSetting",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
}
|
||||
}
|
||||
}
|
@ -335,8 +335,8 @@ namespace API.Data.Migrations
|
||||
|
||||
modelBuilder.Entity("API.Entities.ServerSetting", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
b.Property<int>("Key")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<uint>("RowVersion")
|
||||
.IsConcurrencyToken()
|
||||
|
@ -31,20 +31,23 @@ namespace API.Data
|
||||
|
||||
public static async Task SeedSettings(DataContext context)
|
||||
{
|
||||
context.Database.EnsureCreated();
|
||||
|
||||
IList<ServerSetting> defaultSettings = new List<ServerSetting>()
|
||||
{
|
||||
new() {Key = "CacheDirectory", Value = CacheService.CacheDirectory}
|
||||
new() {Key = ServerSettingKey.CacheDirectory, Value = CacheService.CacheDirectory},
|
||||
new () {Key = ServerSettingKey.TaskScan, Value = "daily"}
|
||||
};
|
||||
var settings = await context.ServerSetting.Select(s => s).ToListAsync();
|
||||
|
||||
foreach (var defaultSetting in defaultSettings)
|
||||
{
|
||||
var existing = settings.SingleOrDefault(s => s.Key == defaultSetting.Key);
|
||||
var existing = context.ServerSetting.FirstOrDefault(s => s.Key == defaultSetting.Key);
|
||||
if (existing == null)
|
||||
{
|
||||
settings.Add(defaultSetting);
|
||||
context.ServerSetting.Add(defaultSetting);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
47
API/Data/SettingsRepository.cs
Normal file
47
API/Data/SettingsRepository.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.Entities;
|
||||
using API.Interfaces;
|
||||
using AutoMapper;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data
|
||||
{
|
||||
public class SettingsRepository : ISettingsRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public SettingsRepository(DataContext context, IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Update(ServerSetting settings)
|
||||
{
|
||||
_context.Entry(settings).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public async Task<ServerSettingDto> GetSettingsDtoAsync()
|
||||
{
|
||||
var settings = await _context.ServerSetting
|
||||
.Select(x => x)
|
||||
.AsNoTracking()
|
||||
.ToListAsync();
|
||||
return _mapper.Map<ServerSettingDto>(settings);
|
||||
}
|
||||
|
||||
public Task<ServerSetting> GetSettingAsync(ServerSettingKey key)
|
||||
{
|
||||
return _context.ServerSetting.SingleOrDefaultAsync(x => x.Key == key);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ServerSetting>> GetSettingsAsync()
|
||||
{
|
||||
return await _context.ServerSetting.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ namespace API.Data
|
||||
public ILibraryRepository LibraryRepository => new LibraryRepository(_context, _mapper);
|
||||
|
||||
public IVolumeRepository VolumeRepository => new VolumeRepository(_context, _mapper);
|
||||
|
||||
public ISettingsRepository SettingsRepository => new SettingsRepository(_context, _mapper);
|
||||
|
||||
public async Task<bool> Complete()
|
||||
{
|
||||
|
@ -7,7 +7,7 @@ namespace API.Entities
|
||||
public class ServerSetting : IHasConcurrencyToken
|
||||
{
|
||||
[Key]
|
||||
public string Key { get; set; }
|
||||
public ServerSettingKey Key { get; set; }
|
||||
public string Value { get; set; }
|
||||
|
||||
[ConcurrencyCheck]
|
||||
|
10
API/Entities/ServerSettingKey.cs
Normal file
10
API/Entities/ServerSettingKey.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace API.Entities
|
||||
{
|
||||
public enum ServerSettingKey
|
||||
{
|
||||
TaskScan = 0,
|
||||
CacheDirectory = 1,
|
||||
TaskBackup = 2,
|
||||
LoggingLevel = 3
|
||||
}
|
||||
}
|
41
API/Helpers/Converters/CronConverter.cs
Normal file
41
API/Helpers/Converters/CronConverter.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using Hangfire;
|
||||
|
||||
namespace API.Helpers.Converters
|
||||
{
|
||||
public static class CronConverter
|
||||
{
|
||||
public static readonly IEnumerable<string> Options = new []
|
||||
{
|
||||
"disabled",
|
||||
"daily",
|
||||
"weekly",
|
||||
};
|
||||
public static string ConvertToCronNotation(string source)
|
||||
{
|
||||
string destination = "";
|
||||
destination = source.ToLower() switch
|
||||
{
|
||||
"daily" => Cron.Daily(),
|
||||
"weekly" => Cron.Weekly(),
|
||||
"disabled" => Cron.Never(),
|
||||
"" => Cron.Never(),
|
||||
_ => destination
|
||||
};
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
public static string ConvertFromCronNotation(string cronNotation)
|
||||
{
|
||||
string destination = "";
|
||||
destination = cronNotation.ToLower() switch
|
||||
{
|
||||
"0 0 31 2 *" => "disabled",
|
||||
_ => destination
|
||||
};
|
||||
|
||||
return destination;
|
||||
}
|
||||
}
|
||||
}
|
@ -9,16 +9,24 @@ namespace API.Helpers.Converters
|
||||
{
|
||||
public ServerSettingDto Convert(IEnumerable<ServerSetting> source, ServerSettingDto destination, ResolutionContext context)
|
||||
{
|
||||
destination = new ServerSettingDto();
|
||||
destination ??= new ServerSettingDto();
|
||||
foreach (var row in source)
|
||||
{
|
||||
switch (row.Key)
|
||||
{
|
||||
case "CacheDirectory":
|
||||
case ServerSettingKey.CacheDirectory:
|
||||
destination.CacheDirectory = row.Value;
|
||||
break;
|
||||
default:
|
||||
case ServerSettingKey.TaskScan:
|
||||
destination.TaskScan = row.Value;
|
||||
break;
|
||||
case ServerSettingKey.LoggingLevel:
|
||||
destination.LoggingLevel = row.Value;
|
||||
break;
|
||||
case ServerSettingKey.TaskBackup:
|
||||
destination.TaskBackup = row.Value;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
16
API/Interfaces/ISettingsRepository.cs
Normal file
16
API/Interfaces/ISettingsRepository.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs;
|
||||
using API.Entities;
|
||||
|
||||
namespace API.Interfaces
|
||||
{
|
||||
public interface ISettingsRepository
|
||||
{
|
||||
void Update(ServerSetting settings);
|
||||
Task<ServerSettingDto> GetSettingsDtoAsync();
|
||||
Task<ServerSetting> GetSettingAsync(ServerSettingKey key);
|
||||
Task<IEnumerable<ServerSetting>> GetSettingsAsync();
|
||||
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ namespace API.Interfaces
|
||||
IUserRepository UserRepository { get; }
|
||||
ILibraryRepository LibraryRepository { get; }
|
||||
IVolumeRepository VolumeRepository { get; }
|
||||
ISettingsRepository SettingsRepository { get; }
|
||||
Task<bool> Complete();
|
||||
bool HasChanges();
|
||||
}
|
||||
|
@ -74,19 +74,18 @@ namespace API.Services
|
||||
|
||||
private byte[] CreateThumbnail(ZipArchiveEntry entry)
|
||||
{
|
||||
var coverImage = Array.Empty<byte>();
|
||||
try
|
||||
{
|
||||
using var stream = entry.Open();
|
||||
using var thumbnail = Image.ThumbnailStream(stream, ThumbnailWidth);
|
||||
coverImage = thumbnail.WriteToBuffer(".jpg");
|
||||
return thumbnail.WriteToBuffer(".jpg");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "There was a critical error and prevented thumbnail generation. Defaulting to no cover image.");
|
||||
}
|
||||
|
||||
return coverImage;
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
private static byte[] ConvertEntryToByteArray(ZipArchiveEntry entry)
|
||||
|
@ -75,7 +75,6 @@ namespace API.Services
|
||||
|
||||
public void CleanupChapters(int[] chapterIds)
|
||||
{
|
||||
// TODO: Fix this code to work with chapters
|
||||
_logger.LogInformation($"Running Cache cleanup on Volumes");
|
||||
|
||||
foreach (var chapter in chapterIds)
|
||||
@ -112,7 +111,7 @@ namespace API.Services
|
||||
{
|
||||
var path = GetCachePath(chapter.Id);
|
||||
// TODO: GetFiles should only get image files.
|
||||
var files = _directoryService.GetFiles(path);
|
||||
var files = _directoryService.GetFiles(path);
|
||||
Array.Sort(files, _numericComparer);
|
||||
|
||||
return (files.ElementAt(page - pagesSoFar), mangaFile);
|
||||
|
@ -4,15 +4,12 @@ using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Extensions;
|
||||
using API.Interfaces;
|
||||
using API.Parser;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NetVips;
|
||||
|
||||
namespace API.Services
|
||||
{
|
||||
@ -61,11 +58,11 @@ namespace API.Services
|
||||
var totalFiles = 0;
|
||||
foreach (var folderPath in library.Folders)
|
||||
{
|
||||
// if (!forceUpdate && Directory.GetLastWriteTime(folderPath.Path) <= folderPath.LastScanned)
|
||||
// {
|
||||
// _logger.LogDebug($"{folderPath.Path} hasn't been updated since last scan. Skipping.");
|
||||
// continue;
|
||||
// }
|
||||
if (!forceUpdate && Directory.GetLastWriteTime(folderPath.Path) <= folderPath.LastScanned)
|
||||
{
|
||||
_logger.LogDebug($"{folderPath.Path} hasn't been updated since last scan. Skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
totalFiles += DirectoryService.TraverseTreeParallelForEach(folderPath.Path, (f) =>
|
||||
@ -163,7 +160,7 @@ namespace API.Services
|
||||
{
|
||||
if (info.Series == string.Empty) return;
|
||||
|
||||
_scannedSeries.AddOrUpdate(info.Series, new List<ParserInfo>() {info}, (key, oldValue) =>
|
||||
_scannedSeries.AddOrUpdate(info.Series, new List<ParserInfo>() {info}, (_, oldValue) =>
|
||||
{
|
||||
oldValue ??= new List<ParserInfo>();
|
||||
if (!oldValue.Contains(info))
|
||||
@ -234,94 +231,6 @@ namespace API.Services
|
||||
return forceUpdate || coverImage == null || !coverImage.Any();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates or Updates volumes for a given series
|
||||
/// </summary>
|
||||
/// <param name="series">Series wanting to be updated</param>
|
||||
/// <param name="infos">Parser info</param>
|
||||
/// <param name="forceUpdate">Forces metadata update (cover image) even if it's already been set.</param>
|
||||
/// <returns>Updated Volumes for given series</returns>
|
||||
private ICollection<Volume> UpdateVolumes(Series series, ParserInfo[] infos, bool forceUpdate)
|
||||
{
|
||||
ICollection<Volume> volumes = new List<Volume>();
|
||||
IList<Volume> existingVolumes = _unitOfWork.SeriesRepository.GetVolumes(series.Id).ToList();
|
||||
|
||||
//var justVolumes = infos.Select(pi => pi.Chapters == "0");
|
||||
|
||||
|
||||
foreach (var info in infos)
|
||||
{
|
||||
var existingVolume = existingVolumes.SingleOrDefault(v => v.Name == info.Volumes);
|
||||
if (existingVolume != null)
|
||||
{
|
||||
//var existingFile = existingVolume.Files.SingleOrDefault(f => f.FilePath == info.FullFilePath);
|
||||
var existingFile = new MangaFile();
|
||||
if (existingFile != null)
|
||||
{
|
||||
//existingFile.Chapter = Parser.Parser.MinimumNumberFromRange(info.Chapters);
|
||||
existingFile.Format = info.Format;
|
||||
existingFile.NumberOfPages = _archiveService.GetNumberOfPagesFromArchive(info.FullFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (info.Format == MangaFormat.Archive)
|
||||
{
|
||||
// existingVolume.Files.Add(CreateMangaFile(info));
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogDebug($"Ignoring {info.Filename} as it is not an archive.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
volumes.Add(existingVolume);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create New Volume
|
||||
existingVolume = volumes.SingleOrDefault(v => v.Name == info.Volumes);
|
||||
if (existingVolume != null)
|
||||
{
|
||||
//existingVolume.Files.Add(CreateMangaFile(info));
|
||||
}
|
||||
else
|
||||
{
|
||||
var vol = new Volume()
|
||||
{
|
||||
Name = info.Volumes,
|
||||
Number = Parser.Parser.MinimumNumberFromRange(info.Volumes),
|
||||
// Files = new List<MangaFile>()
|
||||
// {
|
||||
// CreateMangaFile(info)
|
||||
// }
|
||||
};
|
||||
volumes.Add(vol);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Adding volume {volumes.Last().Number} with File: {info.Filename}");
|
||||
}
|
||||
|
||||
foreach (var volume in volumes)
|
||||
{
|
||||
// if (forceUpdate || volume.CoverImage == null || !volume.Files.Any())
|
||||
// {
|
||||
// var firstFile = volume.Files.OrderBy(x => x.Chapter).FirstOrDefault();
|
||||
// if (firstFile != null) volume.CoverImage = _archiveService.GetCoverImage(firstFile.FilePath, true);
|
||||
// }
|
||||
|
||||
//volume.Pages = volume.Files.Sum(x => x.NumberOfPages);
|
||||
}
|
||||
|
||||
return volumes;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
@ -345,7 +254,7 @@ namespace API.Services
|
||||
};
|
||||
|
||||
chapter.Files ??= new List<MangaFile>();
|
||||
var existingFile = chapter?.Files.SingleOrDefault(f => f.FilePath == info.FullFilePath);
|
||||
var existingFile = chapter.Files.SingleOrDefault(f => f.FilePath == info.FullFilePath);
|
||||
if (existingFile != null)
|
||||
{
|
||||
existingFile.Format = info.Format;
|
||||
@ -376,7 +285,8 @@ namespace API.Services
|
||||
|
||||
if (ShouldFindCoverImage(forceUpdate, chapter.CoverImage))
|
||||
{
|
||||
var firstFile = chapter?.Files.OrderBy(x => x.Chapter).FirstOrDefault();
|
||||
chapter.Files ??= new List<MangaFile>();
|
||||
var firstFile = chapter.Files.OrderBy(x => x.Chapter).FirstOrDefault();
|
||||
if (firstFile != null) chapter.CoverImage = _archiveService.GetCoverImage(firstFile.FilePath, true);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
using API.Interfaces;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using API.Entities;
|
||||
using API.Helpers.Converters;
|
||||
using API.Interfaces;
|
||||
using Hangfire;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@ -9,17 +13,30 @@ namespace API.Services
|
||||
private readonly ICacheService _cacheService;
|
||||
private readonly ILogger<TaskScheduler> _logger;
|
||||
private readonly IScannerService _scannerService;
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
public BackgroundJobServer Client => new BackgroundJobServer();
|
||||
|
||||
public TaskScheduler(ICacheService cacheService, ILogger<TaskScheduler> logger, IScannerService scannerService)
|
||||
public TaskScheduler(ICacheService cacheService, ILogger<TaskScheduler> logger, IScannerService scannerService, IUnitOfWork unitOfWork)
|
||||
{
|
||||
_cacheService = cacheService;
|
||||
_logger = logger;
|
||||
_scannerService = scannerService;
|
||||
_unitOfWork = unitOfWork;
|
||||
|
||||
_logger.LogInformation("Scheduling/Updating cache cleanup on a daily basis.");
|
||||
RecurringJob.AddOrUpdate(() => _cacheService.Cleanup(), Cron.Daily);
|
||||
RecurringJob.AddOrUpdate(() => _scannerService.ScanLibraries(), Cron.Daily);
|
||||
var setting = Task.Run(() => _unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.TaskScan)).Result;
|
||||
if (setting != null)
|
||||
{
|
||||
RecurringJob.AddOrUpdate(() => _scannerService.ScanLibraries(), () => CronConverter.ConvertToCronNotation(setting.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
RecurringJob.AddOrUpdate(() => _cacheService.Cleanup(), Cron.Daily);
|
||||
RecurringJob.AddOrUpdate(() => _scannerService.ScanLibraries(), Cron.Daily);
|
||||
}
|
||||
|
||||
//JobStorage.Current.GetMonitoringApi().
|
||||
|
||||
}
|
||||
|
||||
public void ScanSeries(int libraryId, int seriesId)
|
||||
|
@ -46,8 +46,6 @@ namespace API
|
||||
app.UseHangfireDashboard();
|
||||
}
|
||||
|
||||
//app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
// Ordering is important. Cors, authentication, authorization
|
||||
|
Loading…
x
Reference in New Issue
Block a user