TrangaSettings as static field in Tranga instead of Static class

This commit is contained in:
2025-07-03 17:30:58 +02:00
parent 6f5823596a
commit 681d56710a
12 changed files with 90 additions and 200 deletions

View File

@ -138,8 +138,8 @@ public class MangaController(IServiceScope scope) : Controller
{ {
if (Tranga.GetRunningWorkers().Any(worker => worker is DownloadCoverFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId)) if (Tranga.GetRunningWorkers().Any(worker => worker is DownloadCoverFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId))
{ {
Response.Headers.Append("Retry-After", $"{TrangaSettings.workCycleTimeout * 2 / 1000:D}"); Response.Headers.Append("Retry-After", $"{Tranga.Settings.WorkCycleTimeoutMs * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.workCycleTimeout * 2 / 1000); return StatusCode(Status503ServiceUnavailable, Tranga.Settings.WorkCycleTimeoutMs * 2 / 1000);
} }
else else
return NoContent(); return NoContent();
@ -258,8 +258,8 @@ public class MangaController(IServiceScope scope) : Controller
{ {
if (Tranga.GetRunningWorkers().Any(worker => worker is RetrieveMangaChaptersFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId && w.State < WorkerExecutionState.Completed)) if (Tranga.GetRunningWorkers().Any(worker => worker is RetrieveMangaChaptersFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId && w.State < WorkerExecutionState.Completed))
{ {
Response.Headers.Append("Retry-After", $"{TrangaSettings.workCycleTimeout * 2 / 1000:D}"); Response.Headers.Append("Retry-After", $"{Tranga.Settings.WorkCycleTimeoutMs * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.workCycleTimeout * 2/ 1000); return StatusCode(Status503ServiceUnavailable, Tranga.Settings.WorkCycleTimeoutMs * 2/ 1000);
}else }else
return Ok(0); return Ok(0);
} }
@ -297,8 +297,8 @@ public class MangaController(IServiceScope scope) : Controller
{ {
if (Tranga.GetRunningWorkers().Any(worker => worker is RetrieveMangaChaptersFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId && w.State < WorkerExecutionState.Completed)) if (Tranga.GetRunningWorkers().Any(worker => worker is RetrieveMangaChaptersFromMangaconnectorWorker w && w.MangaConnectorId.ObjId == MangaId && w.State < WorkerExecutionState.Completed))
{ {
Response.Headers.Append("Retry-After", $"{TrangaSettings.workCycleTimeout * 2 / 1000:D}"); Response.Headers.Append("Retry-After", $"{Tranga.Settings.WorkCycleTimeoutMs * 2 / 1000:D}");
return StatusCode(Status503ServiceUnavailable, TrangaSettings.workCycleTimeout * 2/ 1000); return StatusCode(Status503ServiceUnavailable, Tranga.Settings.WorkCycleTimeoutMs * 2/ 1000);
}else }else
return NoContent(); return NoContent();
} }

View File

@ -3,7 +3,6 @@ using API.Schema.MangaContext;
using API.Workers; using API.Workers;
using Asp.Versioning; using Asp.Versioning;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using static Microsoft.AspNetCore.Http.StatusCodes; using static Microsoft.AspNetCore.Http.StatusCodes;
// ReSharper disable InconsistentNaming // ReSharper disable InconsistentNaming
@ -15,14 +14,14 @@ namespace API.Controllers;
public class SettingsController(IServiceScope scope) : Controller public class SettingsController(IServiceScope scope) : Controller
{ {
/// <summary> /// <summary>
/// Get all Settings /// Get all <see cref="Tranga.Settings"/>
/// </summary> /// </summary>
/// <response code="200"></response> /// <response code="200"></response>
[HttpGet] [HttpGet]
[ProducesResponseType<JObject>(Status200OK, "application/json")] [ProducesResponseType<TrangaSettings>(Status200OK, "application/json")]
public IActionResult GetSettings() public IActionResult GetSettings()
{ {
return Ok(JObject.Parse(TrangaSettings.Serialize())); return Ok(Tranga.Settings);
} }
/// <summary> /// <summary>
@ -33,7 +32,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<string>(Status200OK, "text/plain")] [ProducesResponseType<string>(Status200OK, "text/plain")]
public IActionResult GetUserAgent() public IActionResult GetUserAgent()
{ {
return Ok(TrangaSettings.userAgent); return Ok(Tranga.Settings.UserAgent);
} }
/// <summary> /// <summary>
@ -44,7 +43,8 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
public IActionResult SetUserAgent([FromBody]string userAgent) public IActionResult SetUserAgent([FromBody]string userAgent)
{ {
TrangaSettings.UpdateUserAgent(userAgent); //TODO Validate
Tranga.Settings.SetUserAgent(userAgent);
return Ok(); return Ok();
} }
@ -56,7 +56,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
public IActionResult ResetUserAgent() public IActionResult ResetUserAgent()
{ {
TrangaSettings.UpdateUserAgent(TrangaSettings.DefaultUserAgent); Tranga.Settings.SetUserAgent(TrangaSettings.DefaultUserAgent);
return Ok(); return Ok();
} }
@ -68,7 +68,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<Dictionary<RequestType,int>>(Status200OK, "application/json")] [ProducesResponseType<Dictionary<RequestType,int>>(Status200OK, "application/json")]
public IActionResult GetRequestLimits() public IActionResult GetRequestLimits()
{ {
return Ok(TrangaSettings.requestLimits); return Ok(Tranga.Settings.RequestLimits);
} }
/// <summary> /// <summary>
@ -96,7 +96,7 @@ public class SettingsController(IServiceScope scope) : Controller
{ {
if (requestLimit <= 0) if (requestLimit <= 0)
return BadRequest(); return BadRequest();
TrangaSettings.UpdateRequestLimit(RequestType, requestLimit); Tranga.Settings.SetRequestLimit(RequestType, requestLimit);
return Ok(); return Ok();
} }
@ -108,7 +108,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<string>(Status200OK)] [ProducesResponseType<string>(Status200OK)]
public IActionResult ResetRequestLimits(RequestType RequestType) public IActionResult ResetRequestLimits(RequestType RequestType)
{ {
TrangaSettings.UpdateRequestLimit(RequestType, TrangaSettings.DefaultRequestLimits[RequestType]); Tranga.Settings.SetRequestLimit(RequestType, TrangaSettings.DefaultRequestLimits[RequestType]);
return Ok(); return Ok();
} }
@ -120,35 +120,35 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<string>(Status200OK)] [ProducesResponseType<string>(Status200OK)]
public IActionResult ResetRequestLimits() public IActionResult ResetRequestLimits()
{ {
TrangaSettings.ResetRequestLimits(); Tranga.Settings.ResetRequestLimits();
return Ok(); return Ok();
} }
/// <summary> /// <summary>
/// Returns Level of Image-Compression for Images /// Returns Level of Image-Compression for Images
/// </summary> /// </summary>
/// <response code="200">JPEG compression-level as Integer</response> /// <response code="200">JPEG ImageCompression-level as Integer</response>
[HttpGet("ImageCompression")] [HttpGet("ImageCompressionLevel")]
[ProducesResponseType<int>(Status200OK, "text/plain")] [ProducesResponseType<int>(Status200OK, "text/plain")]
public IActionResult GetImageCompression() public IActionResult GetImageCompression()
{ {
return Ok(TrangaSettings.compression); return Ok(Tranga.Settings.ImageCompression);
} }
/// <summary> /// <summary>
/// Set the Image-Compression-Level for Images /// Set the Image-Compression-Level for Images
/// </summary> /// </summary>
/// <param name="level">100 to disable, 0-99 for JPEG compression-Level</param> /// <param name="level">100 to disable, 0-99 for JPEG ImageCompression-Level</param>
/// <response code="200"></response> /// <response code="200"></response>
/// <response code="400">Level outside permitted range</response> /// <response code="400">Level outside permitted range</response>
[HttpPatch("ImageCompression")] [HttpPatch("ImageCompressionLevel/{level}")]
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
[ProducesResponseType(Status400BadRequest)] [ProducesResponseType(Status400BadRequest)]
public IActionResult SetImageCompression([FromBody]int level) public IActionResult SetImageCompression(int level)
{ {
if (level < 1 || level > 100) if (level < 1 || level > 100)
return BadRequest(); return BadRequest();
TrangaSettings.UpdateCompressImages(level); Tranga.Settings.UpdateImageCompression(level);
return Ok(); return Ok();
} }
@ -160,7 +160,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<bool>(Status200OK, "text/plain")] [ProducesResponseType<bool>(Status200OK, "text/plain")]
public IActionResult GetBwImagesToggle() public IActionResult GetBwImagesToggle()
{ {
return Ok(TrangaSettings.bwImages); return Ok(Tranga.Settings.BlackWhiteImages);
} }
/// <summary> /// <summary>
@ -168,37 +168,11 @@ public class SettingsController(IServiceScope scope) : Controller
/// </summary> /// </summary>
/// <param name="enabled">true to enable</param> /// <param name="enabled">true to enable</param>
/// <response code="200"></response> /// <response code="200"></response>
[HttpPatch("BWImages")] [HttpPatch("BWImages/{enabled}")]
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
public IActionResult SetBwImagesToggle([FromBody]bool enabled) public IActionResult SetBwImagesToggle(bool enabled)
{ {
TrangaSettings.UpdateBwImages(enabled); Tranga.Settings.SetBlackWhiteImageEnabled(enabled);
return Ok();
}
/// <summary>
/// Get state of April Fools Mode
/// </summary>
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
/// <response code="200">True if enabled</response>
[HttpGet("AprilFoolsMode")]
[ProducesResponseType<bool>(Status200OK, "text/plain")]
public IActionResult GetAprilFoolsMode()
{
return Ok(TrangaSettings.aprilFoolsMode);
}
/// <summary>
/// Enable/Disable April Fools Mode
/// </summary>
/// <remarks>April Fools Mode disables all downloads on April 1st</remarks>
/// <param name="enabled">true to enable</param>
/// <response code="200"></response>
[HttpPatch("AprilFoolsMode")]
[ProducesResponseType(Status200OK)]
public IActionResult SetAprilFoolsMode([FromBody]bool enabled)
{
TrangaSettings.UpdateAprilFoolsMode(enabled);
return Ok(); return Ok();
} }
@ -224,7 +198,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType<string>(Status200OK, "text/plain")] [ProducesResponseType<string>(Status200OK, "text/plain")]
public IActionResult GetCustomNamingScheme() public IActionResult GetCustomNamingScheme()
{ {
return Ok(TrangaSettings.chapterNamingScheme); return Ok(Tranga.Settings.ChapterNamingScheme);
} }
/// <summary> /// <summary>
@ -250,7 +224,7 @@ public class SettingsController(IServiceScope scope) : Controller
MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>(); MangaContext context = scope.ServiceProvider.GetRequiredService<MangaContext>();
Dictionary<Chapter, string> oldPaths = context.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath); Dictionary<Chapter, string> oldPaths = context.Chapters.ToDictionary(c => c, c => c.FullArchiveFilePath);
TrangaSettings.UpdateChapterNamingScheme(namingScheme); Tranga.Settings.SetChapterNamingScheme(namingScheme);
MoveFileOrFolderWorker[] newJobs = oldPaths MoveFileOrFolderWorker[] newJobs = oldPaths
.Select(kv => new MoveFileOrFolderWorker(kv.Value, kv.Key.FullArchiveFilePath)).ToArray(); .Select(kv => new MoveFileOrFolderWorker(kv.Value, kv.Key.FullArchiveFilePath)).ToArray();
Tranga.AddWorkers(newJobs); Tranga.AddWorkers(newJobs);
@ -267,7 +241,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
public IActionResult SetFlareSolverrUrl([FromBody]string flareSolverrUrl) public IActionResult SetFlareSolverrUrl([FromBody]string flareSolverrUrl)
{ {
TrangaSettings.UpdateFlareSolverrUrl(flareSolverrUrl); Tranga.Settings.SetFlareSolverrUrl(flareSolverrUrl);
return Ok(); return Ok();
} }
@ -279,7 +253,7 @@ public class SettingsController(IServiceScope scope) : Controller
[ProducesResponseType(Status200OK)] [ProducesResponseType(Status200OK)]
public IActionResult ClearFlareSolverrUrl() public IActionResult ClearFlareSolverrUrl()
{ {
TrangaSettings.UpdateFlareSolverrUrl(string.Empty); Tranga.Settings.SetFlareSolverrUrl(string.Empty);
return Ok(); return Ok();
} }

View File

@ -16,14 +16,14 @@ public abstract class DownloadClient
public RequestResult MakeRequest(string url, RequestType requestType, string? referrer = null, string? clickButton = null) public RequestResult MakeRequest(string url, RequestType requestType, string? referrer = null, string? clickButton = null)
{ {
Log.Debug($"Requesting {requestType} {url}"); Log.Debug($"Requesting {requestType} {url}");
if (!TrangaSettings.requestLimits.ContainsKey(requestType)) if (!Tranga.Settings.RequestLimits.ContainsKey(requestType))
{ {
return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null); return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null);
} }
int rateLimit = TrangaSettings.userAgent == TrangaSettings.DefaultUserAgent int rateLimit = Tranga.Settings.UserAgent == TrangaSettings.DefaultUserAgent
? TrangaSettings.DefaultRequestLimits[requestType] ? TrangaSettings.DefaultRequestLimits[requestType]
: TrangaSettings.requestLimits[requestType]; : Tranga.Settings.RequestLimits[requestType];
TimeSpan timeBetweenRequests = TimeSpan.FromMinutes(1).Divide(rateLimit); TimeSpan timeBetweenRequests = TimeSpan.FromMinutes(1).Divide(rateLimit);
DateTime now = DateTime.Now; DateTime now = DateTime.Now;

View File

@ -18,13 +18,13 @@ public class FlareSolverrDownloadClient : DownloadClient
Log.Warn("Client can not click button"); Log.Warn("Client can not click button");
if(referrer is not null) if(referrer is not null)
Log.Warn("Client can not set referrer"); Log.Warn("Client can not set referrer");
if (TrangaSettings.flareSolverrUrl == string.Empty) if (Tranga.Settings.FlareSolverrUrl == string.Empty)
{ {
Log.Error("FlareSolverr URL is empty"); Log.Error("FlareSolverr URL is empty");
return new(HttpStatusCode.InternalServerError, null, Stream.Null); return new(HttpStatusCode.InternalServerError, null, Stream.Null);
} }
Uri flareSolverrUri = new (TrangaSettings.flareSolverrUrl); Uri flareSolverrUri = new (Tranga.Settings.FlareSolverrUrl);
if (flareSolverrUri.Segments.Last() != "v1") if (flareSolverrUri.Segments.Last() != "v1")
flareSolverrUri = new UriBuilder(flareSolverrUri) flareSolverrUri = new UriBuilder(flareSolverrUri)
{ {
@ -35,7 +35,7 @@ public class FlareSolverrDownloadClient : DownloadClient
{ {
Timeout = TimeSpan.FromSeconds(10), Timeout = TimeSpan.FromSeconds(10),
DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher,
DefaultRequestHeaders = { { "User-Agent", TrangaSettings.userAgent } } DefaultRequestHeaders = { { "User-Agent", Tranga.Settings.UserAgent } }
}; };
JObject requestObj = new() JObject requestObj = new()

View File

@ -12,7 +12,7 @@ internal class HttpDownloadClient : DownloadClient
HttpClient client = new(); HttpClient client = new();
client.Timeout = TimeSpan.FromSeconds(10); client.Timeout = TimeSpan.FromSeconds(10);
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
client.DefaultRequestHeaders.Add("User-Agent", TrangaSettings.userAgent); client.DefaultRequestHeaders.Add("User-Agent", Tranga.Settings.UserAgent);
HttpResponseMessage? response; HttpResponseMessage? response;
Uri uri = new(url); Uri uri = new(url);
HttpRequestMessage requestMessage = new(HttpMethod.Get, uri); HttpRequestMessage requestMessage = new(HttpMethod.Get, uri);

View File

@ -97,8 +97,7 @@ app.MapControllers()
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(options => app.UseSwaggerUI(options =>
{ {
options.SwaggerEndpoint( options.SwaggerEndpoint($"/swagger/v2/swagger.json", "v2");
$"/swagger/v2/swagger.json", "v2");
}); });
app.UseHttpsRedirection(); app.UseHttpsRedirection();
@ -119,7 +118,7 @@ using (IServiceScope scope = app.Services.CreateScope())
MangaConnector[] newConnectors = connectors.Where(c => !context.MangaConnectors.Contains(c)).ToArray(); MangaConnector[] newConnectors = connectors.Where(c => !context.MangaConnectors.Contains(c)).ToArray();
context.MangaConnectors.AddRange(newConnectors); context.MangaConnectors.AddRange(newConnectors);
if (!context.FileLibraries.Any()) if (!context.FileLibraries.Any())
context.FileLibraries.Add(new FileLibrary(TrangaSettings.downloadLocation, "Default FileLibrary")); context.FileLibraries.Add(new FileLibrary(Tranga.Settings.DownloadLocation, "Default FileLibrary"));
context.Sync(); context.Sync();
} }

View File

@ -108,7 +108,7 @@ public class Chapter : Identifiable, IComparable<Chapter>
private static readonly Regex ReplaceRexx = new(@"%([a-zA-Z])|(.+?)"); private static readonly Regex ReplaceRexx = new(@"%([a-zA-Z])|(.+?)");
private string GetArchiveFilePath() private string GetArchiveFilePath()
{ {
string archiveNamingScheme = TrangaSettings.chapterNamingScheme; string archiveNamingScheme = Tranga.Settings.ChapterNamingScheme;
StringBuilder stringBuilder = new(); StringBuilder stringBuilder = new();
foreach (Match nullable in NullableRex.Matches(archiveNamingScheme)) foreach (Match nullable in NullableRex.Matches(archiveNamingScheme))
{ {

View File

@ -34,7 +34,7 @@ public class NotificationConnector(string name, string url, Dictionary<string, s
[NotMapped] [NotMapped]
private readonly HttpClient Client = new() private readonly HttpClient Client = new()
{ {
DefaultRequestHeaders = { { "User-Agent", TrangaSettings.userAgent } } DefaultRequestHeaders = { { "User-Agent", Tranga.Settings.UserAgent } }
}; };
[JsonIgnore] [JsonIgnore]

View File

@ -21,6 +21,7 @@ public static class Tranga
public static Thread PeriodicWorkerStarterThread { get; } = new (WorkerStarter); public static Thread PeriodicWorkerStarterThread { get; } = new (WorkerStarter);
private static readonly ILog Log = LogManager.GetLogger(typeof(Tranga)); private static readonly ILog Log = LogManager.GetLogger(typeof(Tranga));
internal static readonly MetadataFetcher[] MetadataFetchers = [new MyAnimeList()]; internal static readonly MetadataFetcher[] MetadataFetchers = [new MyAnimeList()];
internal static TrangaSettings Settings = TrangaSettings.Load();
internal static void StartLogger() internal static void StartLogger()
{ {
@ -80,7 +81,7 @@ public static class Tranga
scopedWorker.SetScope(serviceProvider.CreateScope()); scopedWorker.SetScope(serviceProvider.CreateScope());
RunningWorkers.Add(worker, worker.DoWork()); RunningWorkers.Add(worker, worker.DoWork());
} }
Thread.Sleep(TrangaSettings.workCycleTimeout); Thread.Sleep(Settings.WorkCycleTimeoutMs);
} }
} }

View File

@ -6,16 +6,22 @@ using Newtonsoft.Json.Linq;
namespace API; namespace API;
public static class TrangaSettings public struct TrangaSettings()
{ {
public static string downloadLocation { get; private set; } = (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/Obj" : Path.Join(Directory.GetCurrentDirectory(), "Downloads"));
public static string workingDirectory { get; private set; } = Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api"); [JsonIgnore]
public static string workingDirectory => Path.Join(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/usr/share" : Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "tranga-api");
[JsonIgnore]
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore]
public static string coverImageCache => Path.Join(workingDirectory, "imageCache");
public string DownloadLocation => RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "/Manga" : Path.Join(Directory.GetCurrentDirectory(), "Manga");
[JsonIgnore] [JsonIgnore]
internal static readonly string DefaultUserAgent = $"Tranga/2.0 ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")})"; internal static readonly string DefaultUserAgent = $"Tranga/2.0 ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")})";
public static string userAgent { get; private set; } = DefaultUserAgent; public string UserAgent { get; private set; } = DefaultUserAgent;
public static int compression{ get; private set; } = 40; public int ImageCompression{ get; private set; } = 40;
public static bool bwImages { get; private set; } = false; public bool BlackWhiteImages { get; private set; } = false;
public static string flareSolverrUrl { get; private set; } = string.Empty; public string FlareSolverrUrl { get; private set; } = string.Empty;
/// <summary> /// <summary>
/// Placeholders: /// Placeholders:
/// %M Obj Name /// %M Obj Name
@ -30,13 +36,8 @@ public static class TrangaSettings
/// ?_(...) replace _ with a value from above: /// ?_(...) replace _ with a value from above:
/// Everything inside the braces will only be added if the value of %_ is not null /// Everything inside the braces will only be added if the value of %_ is not null
/// </summary> /// </summary>
public static string chapterNamingScheme { get; private set; } = "%M - ?V(Vol.%V )Ch.%C?T( - %T)"; public string ChapterNamingScheme { get; private set; } = "%M - ?V(Vol.%V )Ch.%C?T( - %T)";
[JsonIgnore] public int WorkCycleTimeoutMs { get; private set; } = 20000;
public static string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore]
public static string coverImageCache => Path.Join(workingDirectory, "imageCache");
public static bool aprilFoolsMode { get; private set; } = true;
public static int workCycleTimeout { get; private set; } = 20000;
[JsonIgnore] [JsonIgnore]
internal static readonly Dictionary<RequestType, int> DefaultRequestLimits = new () internal static readonly Dictionary<RequestType, int> DefaultRequestLimits = new ()
{ {
@ -47,142 +48,57 @@ public static class TrangaSettings
{RequestType.MangaCover, 60}, {RequestType.MangaCover, 60},
{RequestType.Default, 60} {RequestType.Default, 60}
}; };
public static Dictionary<RequestType, int> requestLimits { get; private set; } = DefaultRequestLimits; public Dictionary<RequestType, int> RequestLimits { get; private set; } = DefaultRequestLimits;
public static TimeSpan NotificationUrgencyDelay(NotificationUrgency urgency) => urgency switch public static TrangaSettings Load()
{ {
NotificationUrgency.High => TimeSpan.Zero, return JsonConvert.DeserializeObject<TrangaSettings>(File.ReadAllText(settingsFilePath));
NotificationUrgency.Normal => TimeSpan.FromMinutes(5),
NotificationUrgency.Low => TimeSpan.FromMinutes(10),
_ => TimeSpan.FromHours(1)
}; //TODO make this a setting?
public static void Load()
{
if(File.Exists(settingsFilePath))
Deserialize(File.ReadAllText(settingsFilePath));
else return;
Directory.CreateDirectory(downloadLocation);
ExportSettings();
} }
public static void UpdateAprilFoolsMode(bool enabled) public void Save()
{ {
aprilFoolsMode = enabled; File.WriteAllText(settingsFilePath, JsonConvert.SerializeObject(this));
ExportSettings();
} }
public static void UpdateCompressImages(int value) public void SetUserAgent(string value)
{ {
compression = int.Clamp(value, 1, 100); this.UserAgent = value;
ExportSettings(); Save();
} }
public static void UpdateBwImages(bool enabled) public void SetRequestLimit(RequestType type, int value)
{ {
bwImages = enabled; this.RequestLimits[type] = value;
ExportSettings(); Save();
} }
public static void UpdateUserAgent(string? customUserAgent) public void ResetRequestLimits()
{ {
userAgent = customUserAgent ?? DefaultUserAgent; this.RequestLimits = DefaultRequestLimits;
ExportSettings(); Save();
} }
public static void UpdateRequestLimit(RequestType requestType, int newLimit) public void UpdateImageCompression(int value)
{ {
requestLimits[requestType] = newLimit; this.ImageCompression = value;
ExportSettings(); Save();
} }
public static void UpdateChapterNamingScheme(string namingScheme) public void SetBlackWhiteImageEnabled(bool enabled)
{ {
chapterNamingScheme = namingScheme; this.BlackWhiteImages = enabled;
ExportSettings(); Save();
} }
public static void UpdateFlareSolverrUrl(string url) public void SetChapterNamingScheme(string scheme)
{ {
flareSolverrUrl = url; this.ChapterNamingScheme = scheme;
ExportSettings(); Save();
} }
public static void ResetRequestLimits() public void SetFlareSolverrUrl(string url)
{ {
requestLimits = DefaultRequestLimits; this.FlareSolverrUrl = url;
ExportSettings(); Save();
}
public static void ExportSettings()
{
if (File.Exists(settingsFilePath))
{
while(IsFileInUse(settingsFilePath))
Thread.Sleep(100);
}
else
Directory.CreateDirectory(new FileInfo(settingsFilePath).DirectoryName!);
File.WriteAllText(settingsFilePath, Serialize());
}
internal static bool IsFileInUse(string filePath)
{
if (!File.Exists(filePath))
return false;
try
{
using FileStream stream = new (filePath, FileMode.Open, FileAccess.Read, FileShare.None);
stream.Close();
return false;
}
catch (IOException)
{
return true;
}
}
public static JObject AsJObject()
{
JObject jobj = new ();
jobj.Add("downloadLocation", JToken.FromObject(downloadLocation));
jobj.Add("workingDirectory", JToken.FromObject(workingDirectory));
jobj.Add("userAgent", JToken.FromObject(userAgent));
jobj.Add("aprilFoolsMode", JToken.FromObject(aprilFoolsMode));
jobj.Add("requestLimits", JToken.FromObject(requestLimits));
jobj.Add("compression", JToken.FromObject(compression));
jobj.Add("bwImages", JToken.FromObject(bwImages));
jobj.Add("workCycleTimeout", JToken.FromObject(workCycleTimeout));
jobj.Add("chapterNamingScheme", JToken.FromObject(chapterNamingScheme));
jobj.Add("flareSolverrUrl", JToken.FromObject(flareSolverrUrl));
return jobj;
}
public static string Serialize() => AsJObject().ToString();
public static void Deserialize(string serialized)
{
JObject jobj = JObject.Parse(serialized);
if (jobj.TryGetValue("downloadLocation", out JToken? dl))
downloadLocation = dl.Value<string>()!;
if (jobj.TryGetValue("workingDirectory", out JToken? wd))
workingDirectory = wd.Value<string>()!;
if (jobj.TryGetValue("userAgent", out JToken? ua))
userAgent = ua.Value<string>()!;
if (jobj.TryGetValue("aprilFoolsMode", out JToken? afm))
aprilFoolsMode = afm.Value<bool>()!;
if (jobj.TryGetValue("requestLimits", out JToken? rl))
requestLimits = rl.ToObject<Dictionary<RequestType, int>>()!;
if (jobj.TryGetValue("compression", out JToken? ci))
compression = ci.Value<int>()!;
if (jobj.TryGetValue("bwImages", out JToken? bwi))
bwImages = bwi.Value<bool>()!;
if (jobj.TryGetValue("workCycleTimeout", out JToken? snjt))
workCycleTimeout = snjt.Value<int>()!;
if (jobj.TryGetValue("chapterNamingScheme", out JToken? cns))
chapterNamingScheme = cns.Value<string>()!;
if (jobj.TryGetValue("flareSolverrUrl", out JToken? fsu))
flareSolverrUrl = fsu.Value<string>()!;
} }
} }

View File

@ -101,7 +101,7 @@ public abstract class BaseWorker : Identifiable
Log.Info($"Waiting for {MissingDependencies.Count()} Dependencies {this}:\n\t{string.Join("\n\t", MissingDependencies.Select(d => d.ToString()))}"); Log.Info($"Waiting for {MissingDependencies.Count()} Dependencies {this}:\n\t{string.Join("\n\t", MissingDependencies.Select(d => d.ToString()))}");
while (CancellationTokenSource.IsCancellationRequested == false && MissingDependencies.Any()) while (CancellationTokenSource.IsCancellationRequested == false && MissingDependencies.Any())
{ {
Thread.Sleep(TrangaSettings.workCycleTimeout); Thread.Sleep(Tranga.Settings.WorkCycleTimeoutMs);
} }
return [this]; return [this];
} }

View File

@ -96,7 +96,7 @@ public class DownloadChapterFromMangaconnectorWorker(Chapter chapter, IEnumerabl
private void ProcessImage(string imagePath) private void ProcessImage(string imagePath)
{ {
if (!TrangaSettings.bwImages && TrangaSettings.compression == 100) if (!Tranga.Settings.BlackWhiteImages && Tranga.Settings.ImageCompression == 100)
{ {
Log.Debug("No processing requested for image"); Log.Debug("No processing requested for image");
return; return;
@ -107,12 +107,12 @@ public class DownloadChapterFromMangaconnectorWorker(Chapter chapter, IEnumerabl
try try
{ {
using Image image = Image.Load(imagePath); using Image image = Image.Load(imagePath);
if (TrangaSettings.bwImages) if (Tranga.Settings.BlackWhiteImages)
image.Mutate(i => i.ApplyProcessor(new AdaptiveThresholdProcessor())); image.Mutate(i => i.ApplyProcessor(new AdaptiveThresholdProcessor()));
File.Delete(imagePath); File.Delete(imagePath);
image.SaveAsJpeg(imagePath, new JpegEncoder() image.SaveAsJpeg(imagePath, new JpegEncoder()
{ {
Quality = TrangaSettings.compression Quality = Tranga.Settings.ImageCompression
}); });
} }
catch (Exception e) catch (Exception e)