diff --git a/API/Controllers/Requests/PatchLibraryRefreshRecord.cs b/API/Controllers/Requests/PatchLibraryRefreshRecord.cs
new file mode 100644
index 0000000..6e2182f
--- /dev/null
+++ b/API/Controllers/Requests/PatchLibraryRefreshRecord.cs
@@ -0,0 +1,21 @@
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using API.Workers;
+
+namespace API.Controllers.Requests;
+
+public record PatchLibraryRefreshRecord
+{
+ ///
+ /// When to refresh the Library
+ ///
+ [Required]
+ [Description("When to refresh the Library")]
+ public required LibraryRefreshSetting Setting { get; init; }
+
+ ///
+ /// When is selected, update the time between refreshes
+ ///
+ [Description("When WhileDownloadingis selected, update the time between refreshes")]
+ public int? RefreshLibraryWhileDownloadingEveryMinutes { get; init; }
+}
\ No newline at end of file
diff --git a/API/Controllers/SettingsController.cs b/API/Controllers/SettingsController.cs
index c7c9d05..fce6d47 100644
--- a/API/Controllers/SettingsController.cs
+++ b/API/Controllers/SettingsController.cs
@@ -1,4 +1,5 @@
-using API.MangaDownloadClients;
+using API.Controllers.Requests;
+using API.MangaDownloadClients;
using Asp.Versioning;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
@@ -290,4 +291,19 @@ public class SettingsController() : Controller
Tranga.Settings.SetDownloadLanguage(Language);
return TypedResults.Ok();
}
+
+
+ ///
+ /// Sets the time when Libraries are refreshed
+ ///
+ ///
+ [HttpPatch("LibraryRefresh")]
+ [ProducesResponseType(Status200OK)]
+ public Ok SetLibraryRefresh([FromBody]PatchLibraryRefreshRecord requestData)
+ {
+ Tranga.Settings.SetLibraryRefreshSetting(requestData.Setting);
+ if(requestData.RefreshLibraryWhileDownloadingEveryMinutes is { } value)
+ Tranga.Settings.SetRefreshLibraryWhileDownloadingEveryMinutes(value);
+ return TypedResults.Ok();
+ }
}
\ No newline at end of file
diff --git a/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs b/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
index f25e551..f332bfa 100644
--- a/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
+++ b/API/Schema/LibraryContext/LibraryConnectors/LibraryConnector.cs
@@ -2,6 +2,8 @@
using System.ComponentModel.DataAnnotations.Schema;
using log4net;
using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace API.Schema.LibraryContext.LibraryConnectors;
@@ -40,8 +42,16 @@ public abstract class LibraryConnector : Identifiable
internal abstract Task Test(CancellationToken ct);
}
+[JsonConverter(typeof(StringEnumConverter))]
public enum LibraryType : byte
{
+ ///
+ ///
+ ///
Komga = 0,
+
+ ///
+ ///
+ ///
Kavita = 1
}
\ No newline at end of file
diff --git a/API/Schema/MangaContext/Manga.cs b/API/Schema/MangaContext/Manga.cs
index 8a694de..753e569 100644
--- a/API/Schema/MangaContext/Manga.cs
+++ b/API/Schema/MangaContext/Manga.cs
@@ -1,13 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Runtime.InteropServices;
-using System.Text;
using API.Workers;
using Microsoft.EntityFrameworkCore;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Jpeg;
-using SixLabors.ImageSharp.Processing;
-using SixLabors.ImageSharp.Processing.Processors.Transforms;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
using static System.IO.UnixFileMode;
namespace API.Schema.MangaContext;
@@ -138,6 +135,7 @@ public class Manga : Identifiable
public override string ToString() => $"{base.ToString()} {Name}";
}
+[JsonConverter(typeof(StringEnumConverter))]
public enum MangaReleaseStatus : byte
{
Continuing = 0,
diff --git a/API/Schema/NotificationsContext/Notification.cs b/API/Schema/NotificationsContext/Notification.cs
index 0ea5a75..ae4e84e 100644
--- a/API/Schema/NotificationsContext/Notification.cs
+++ b/API/Schema/NotificationsContext/Notification.cs
@@ -1,5 +1,7 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace API.Schema.NotificationsContext;
@@ -48,6 +50,7 @@ public class Notification : Identifiable
public override string ToString() => $"{base.ToString()} {Urgency} {Title} {Message}";
}
+[JsonConverter(typeof(StringEnumConverter))]
public enum NotificationUrgency : byte
{
Low = 1,
diff --git a/API/Tranga.cs b/API/Tranga.cs
index de6563f..f9561c9 100644
--- a/API/Tranga.cs
+++ b/API/Tranga.cs
@@ -146,25 +146,27 @@ public static class Tranga
private static Action DefaultAfterWork(BaseWorker worker, Action? callback = null) => () =>
{
Log.Debug($"DefaultAfterWork {worker}");
- if (RunningWorkers.TryGetValue(worker, out Task? task))
+ try
{
- Log.Debug($"Waiting for Children to exit {worker}");
- task.Wait();
- if (task.IsCompleted)
+ if (RunningWorkers.TryGetValue(worker, out Task? task))
{
- try
+ Log.Debug($"Waiting for Children to exit {worker}");
+ task.Wait();
+ if (task.IsCompleted)
{
+ Log.Debug($"Children done {worker}");
BaseWorker[] newWorkers = task.Result;
Log.Debug($"{worker} created {newWorkers.Length} new Workers.");
AddWorkers(newWorkers);
- }
- catch (Exception e)
- {
- Log.Error(e);
- }
- }else Log.Warn($"Children failed: {worker}");
+ }else
+ Log.Warn($"Children failed: {worker}");
+ }
+ RunningWorkers.Remove(worker, out _);
+ }
+ catch (Exception e)
+ {
+ Log.Error(e);
}
- RunningWorkers.Remove(worker, out _);
callback?.Invoke();
};
diff --git a/API/TrangaSettings.cs b/API/TrangaSettings.cs
index bcbbe44..7b205e4 100644
--- a/API/TrangaSettings.cs
+++ b/API/TrangaSettings.cs
@@ -1,6 +1,8 @@
using System.Runtime.InteropServices;
using API.MangaDownloadClients;
+using API.Workers;
using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace API;
@@ -54,16 +56,20 @@ public struct TrangaSettings()
public int MaxConcurrentWorkers { get; set; } = Math.Max(Environment.ProcessorCount, 4); // Minimum of 4 Tasks, maximum of 1 per Core
+ public LibraryRefreshSetting LibraryRefreshSetting { get; set; } = LibraryRefreshSetting.AfterMangaFinished;
+
+ public int RefreshLibraryWhileDownloadingEveryMinutes { get; set; } = 10;
+
public static TrangaSettings Load()
{
if (!File.Exists(SettingsFilePath))
new TrangaSettings().Save();
- return JsonConvert.DeserializeObject(File.ReadAllText(SettingsFilePath));
+ return JsonConvert.DeserializeObject(File.ReadAllText(SettingsFilePath), new StringEnumConverter());
}
public void Save()
{
- File.WriteAllText(SettingsFilePath, JsonConvert.SerializeObject(this, Formatting.Indented));
+ File.WriteAllText(SettingsFilePath, JsonConvert.SerializeObject(this, Formatting.Indented, new StringEnumConverter()));
}
public void SetUserAgent(string value)
@@ -125,4 +131,16 @@ public struct TrangaSettings()
this.MaxConcurrentWorkers = value;
Save();
}
+
+ public void SetLibraryRefreshSetting(LibraryRefreshSetting setting)
+ {
+ this.LibraryRefreshSetting = setting;
+ Save();
+ }
+
+ public void SetRefreshLibraryWhileDownloadingEveryMinutes(int value)
+ {
+ this.RefreshLibraryWhileDownloadingEveryMinutes = value;
+ Save();
+ }
}
\ No newline at end of file
diff --git a/API/Workers/BaseWorker.cs b/API/Workers/BaseWorker.cs
index 58a2d5b..1208e84 100644
--- a/API/Workers/BaseWorker.cs
+++ b/API/Workers/BaseWorker.cs
@@ -1,5 +1,7 @@
using API.Schema;
using log4net;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
namespace API.Workers;
@@ -116,6 +118,7 @@ public abstract class BaseWorker : Identifiable
}
}
+[JsonConverter(typeof(StringEnumConverter))]
public enum WorkerExecutionState
{
Failed = 0,
diff --git a/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs b/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs
index c03a6b5..f80214f 100644
--- a/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs
+++ b/API/Workers/MangaDownloadWorkers/DownloadChapterFromMangaconnectorWorker.cs
@@ -3,6 +3,7 @@ using System.Runtime.InteropServices;
using API.MangaConnectors;
using API.MangaDownloadClients;
using API.Schema.MangaContext;
+using API.Workers.PeriodicWorkers;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using SixLabors.ImageSharp;
@@ -21,16 +22,16 @@ namespace API.Workers.MangaDownloadWorkers;
public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId chId, IEnumerable? dependsOn = null)
: BaseWorkerWithContext(dependsOn)
{
- internal readonly string MangaConnectorIdId = chId.Key;
+ private readonly string _mangaConnectorIdId = chId.Key;
protected override async Task DoWorkInternal()
{
- Log.Debug($"Downloading chapter for MangaConnectorId {MangaConnectorIdId}...");
+ Log.Debug($"Downloading chapter for MangaConnectorId {_mangaConnectorIdId}...");
// Getting MangaConnector info
if (await DbContext.MangaConnectorToChapter
.Include(id => id.Obj)
.ThenInclude(c => c.ParentManga)
.ThenInclude(m => m.Library)
- .FirstOrDefaultAsync(c => c.Key == MangaConnectorIdId, CancellationToken) is not { } mangaConnectorId)
+ .FirstOrDefaultAsync(c => c.Key == _mangaConnectorIdId, CancellationToken) is not { } mangaConnectorId)
{
Log.Error("Could not get MangaConnectorId.");
return []; //TODO Exception?
@@ -137,8 +138,22 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c
Log.Debug($"Downloaded chapter {chapter}.");
- return [];
+ bool refreshLibrary = await CheckLibraryRefresh();
+ if(refreshLibrary)
+ Log.Info($"Condition {Tranga.Settings.LibraryRefreshSetting} met.");
+
+ return refreshLibrary? [new RefreshLibrariesWorker()] : [];
}
+
+ private async Task CheckLibraryRefresh() => Tranga.Settings.LibraryRefreshSetting switch
+ {
+ LibraryRefreshSetting.AfterAllFinished => await AllDownloadsFinished(),
+ LibraryRefreshSetting.AfterMangaFinished => await DbContext.MangaConnectorToChapter.Include(chId => chId.Obj).Where(chId => chId.UseForDownload).AllAsync(chId => chId.Obj.Downloaded, CancellationToken),
+ LibraryRefreshSetting.AfterEveryChapter => true,
+ LibraryRefreshSetting.WhileDownloading => await AllDownloadsFinished() || DateTime.UtcNow.Subtract(RefreshLibrariesWorker.LastRefresh).TotalMinutes > Tranga.Settings.RefreshLibraryWhileDownloadingEveryMinutes,
+ _ => true
+ };
+ private async Task AllDownloadsFinished() => (await StartNewChapterDownloadsWorker.GetMissingChapters(DbContext, CancellationToken)).Count == 0;
private void ProcessImage(string imagePath)
{
@@ -232,5 +247,5 @@ public class DownloadChapterFromMangaconnectorWorker(MangaConnectorId c
return true;
}
- public override string ToString() => $"{base.ToString()} {MangaConnectorIdId}";
+ public override string ToString() => $"{base.ToString()} {_mangaConnectorIdId}";
}
\ No newline at end of file
diff --git a/API/Workers/PeriodicWorkers/StartNewChapterDownloadsWorker.cs b/API/Workers/PeriodicWorkers/StartNewChapterDownloadsWorker.cs
index 05d386a..3aef412 100644
--- a/API/Workers/PeriodicWorkers/StartNewChapterDownloadsWorker.cs
+++ b/API/Workers/PeriodicWorkers/StartNewChapterDownloadsWorker.cs
@@ -18,10 +18,7 @@ public class StartNewChapterDownloadsWorker(TimeSpan? interval = null, IEnumerab
Log.Debug("Checking for missing chapters...");
// Get missing chapters
- List> missingChapters = await DbContext.MangaConnectorToChapter
- .Include(id => id.Obj)
- .Where(id => id.Obj.Downloaded == false && id.UseForDownload)
- .ToListAsync(CancellationToken);
+ List> missingChapters = await GetMissingChapters(DbContext, CancellationToken);
Log.Debug($"Found {missingChapters.Count} missing downloads.");
@@ -37,4 +34,9 @@ public class StartNewChapterDownloadsWorker(TimeSpan? interval = null, IEnumerab
return newWorkers.ToArray();
}
+
+ internal static async Task>> GetMissingChapters(MangaContext ctx, CancellationToken cancellationToken) => await ctx.MangaConnectorToChapter
+ .Include(id => id.Obj)
+ .Where(id => id.Obj.Downloaded == false && id.UseForDownload)
+ .ToListAsync(cancellationToken);
}
\ No newline at end of file
diff --git a/API/Workers/RefreshLibrariesWorker.cs b/API/Workers/RefreshLibrariesWorker.cs
new file mode 100644
index 0000000..0167afb
--- /dev/null
+++ b/API/Workers/RefreshLibrariesWorker.cs
@@ -0,0 +1,44 @@
+using API.Schema.LibraryContext;
+using API.Schema.LibraryContext.LibraryConnectors;
+using Microsoft.EntityFrameworkCore;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace API.Workers;
+
+public class RefreshLibrariesWorker(IEnumerable? dependsOn = null) : BaseWorkerWithContext(dependsOn)
+{
+ public static DateTime LastRefresh { get; set; } = DateTime.UnixEpoch;
+
+ protected override async Task DoWorkInternal()
+ {
+ Log.Debug("Refreshing libraries...");
+ LastRefresh = DateTime.UtcNow;
+ List libraries = await DbContext.LibraryConnectors.ToListAsync(CancellationToken);
+ foreach (LibraryConnector connector in libraries)
+ await connector.UpdateLibrary(CancellationToken);
+ Log.Debug("Libraries Refreshed...");
+ return [];
+ }
+}
+
+[JsonConverter(typeof(StringEnumConverter))]
+public enum LibraryRefreshSetting : byte
+{
+ ///
+ /// Refresh Libraries after all Manga are downloaded
+ ///
+ AfterAllFinished = 0,
+ ///
+ /// Refresh Libraries after a Manga is downloaded
+ ///
+ AfterMangaFinished = 1,
+ ///
+ /// Refresh Libraries after every download
+ ///
+ AfterEveryChapter = 2,
+ ///
+ /// Refresh Libraries while downloading chapters, every x minutes
+ ///
+ WhileDownloading = 3
+}
\ No newline at end of file
diff --git a/Tranga.sln.DotSettings b/Tranga.sln.DotSettings
index 718e87a..f95497a 100644
--- a/Tranga.sln.DotSettings
+++ b/Tranga.sln.DotSettings
@@ -8,6 +8,7 @@
True
True
True
+ True
True
True
True
@@ -15,4 +16,5 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
\ No newline at end of file