diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 607b7a6..20b8472 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -2,6 +2,10 @@ using System.Net; using System.Runtime.InteropServices; using System.Text.RegularExpressions; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Binarization; using Tranga.Jobs; using static System.IO.UnixFileMode; @@ -216,6 +220,22 @@ public abstract class MangaConnector : GlobalBase return requestResult.statusCode; } + private void ProcessImage(string imagePath) + { + if (!TrangaSettings.bwImages && !TrangaSettings.compressImages) + return; + DateTime start = DateTime.Now; + using Image image = Image.Load(imagePath); + File.Delete(imagePath); + if(TrangaSettings.bwImages) + image.Mutate(i => i.ApplyProcessor(new AdaptiveThresholdProcessor())); + image.SaveAsJpeg(imagePath, new JpegEncoder() + { + Quality = TrangaSettings.compressImages ? 30 : 75 + }); + Log($"Image processing took {DateTime.Now.Subtract(start):s\\.fff} B/W:{TrangaSettings.bwImages} Compress:{TrangaSettings.compressImages}"); + } + protected HttpStatusCode DownloadChapterImages(string[] imageUrls, Chapter chapter, RequestType requestType, string? referrer = null, ProgressToken? progressToken = null) { string saveArchiveFilePath = chapter.GetArchiveFilePath(); @@ -254,11 +274,14 @@ public abstract class MangaConnector : GlobalBase progressToken?.Complete(); return HttpStatusCode.NoContent; } + foreach (string imageUrl in imageUrls) { string extension = imageUrl.Split('.')[^1].Split('?')[0]; - Log($"Downloading image {chapterNum + 1:000}/{imageUrls.Length:000}"); //TODO - HttpStatusCode status = DownloadImage(imageUrl, Path.Join(tempFolder, $"{chapterNum++}.{extension}"), requestType, referrer); + Log($"Downloading image {chapterNum + 1:000}/{imageUrls.Length:000}"); + string imagePath = Path.Join(tempFolder, $"{chapterNum++}.{extension}"); + HttpStatusCode status = DownloadImage(imageUrl, imagePath, requestType, referrer); + ProcessImage(imagePath); Log($"{saveArchiveFilePath} {chapterNum + 1:000}/{imageUrls.Length:000} {status}"); if ((int)status < 200 || (int)status >= 300) { diff --git a/Tranga/Server/Server.cs b/Tranga/Server/Server.cs index acc5644..68e326c 100644 --- a/Tranga/Server/Server.cs +++ b/Tranga/Server/Server.cs @@ -58,6 +58,10 @@ public partial class Server : GlobalBase, IDisposable new ("POST", @"/v2/Settings/RateLimit/([a-zA-Z]+)", PostV2SettingsRateLimitType), new ("GET", @"/v2/Settings/AprilFoolsMode", GetV2SettingsAprilFoolsMode), new ("POST", @"/v2/Settings/AprilFoolsMode", PostV2SettingsAprilFoolsMode), + new ("GET", @"/v2/Settings/CompressImages", GetV2SettingsCompressImages), + new ("POST", @"/v2/Settings/CompressImages", PostV2SettingsCompressImages), + new ("GET", @"/v2/Settings/BWImages", GetV2SettingsBwImages), + new ("POST", @"/v2/Settings/BWImages", PostV2SettingsBwImages), new ("POST", @"/v2/Settings/DownloadLocation", PostV2SettingsDownloadLocation), new ("GET", @"/v2/LibraryConnector", GetV2LibraryConnector), new ("GET", @"/v2/LibraryConnector/Types", GetV2LibraryConnectorTypes), diff --git a/Tranga/Server/v2Settings.cs b/Tranga/Server/v2Settings.cs index f6c1d2c..045312f 100644 --- a/Tranga/Server/v2Settings.cs +++ b/Tranga/Server/v2Settings.cs @@ -86,6 +86,34 @@ public partial class Server return new ValueTuple(HttpStatusCode.OK, null); } + private ValueTuple GetV2SettingsCompressImages(GroupCollection groups, Dictionary requestParameters) + { + return new ValueTuple(HttpStatusCode.OK, TrangaSettings.compressImages); + } + + private ValueTuple PostV2SettingsCompressImages(GroupCollection groups, Dictionary requestParameters) + { + if (!requestParameters.TryGetValue("value", out string? trueFalseStr) || + !bool.TryParse(trueFalseStr, out bool trueFalse)) + return new ValueTuple(HttpStatusCode.InternalServerError, "Errors parsing 'value'"); + TrangaSettings.UpdateCompressImages(trueFalse); + return new ValueTuple(HttpStatusCode.OK, null); + } + + private ValueTuple GetV2SettingsBwImages(GroupCollection groups, Dictionary requestParameters) + { + return new ValueTuple(HttpStatusCode.OK, TrangaSettings.bwImages); + } + + private ValueTuple PostV2SettingsBwImages(GroupCollection groups, Dictionary requestParameters) + { + if (!requestParameters.TryGetValue("value", out string? trueFalseStr) || + !bool.TryParse(trueFalseStr, out bool trueFalse)) + return new ValueTuple(HttpStatusCode.InternalServerError, "Errors parsing 'value'"); + TrangaSettings.UpdateBwImages(trueFalse); + return new ValueTuple(HttpStatusCode.OK, null); + } + private ValueTuple PostV2SettingsDownloadLocation(GroupCollection groups, Dictionary requestParameters) { if (!requestParameters.TryGetValue("location", out string? folderPath)) diff --git a/Tranga/TrangaSettings.cs b/Tranga/TrangaSettings.cs index bf4ee96..16919c8 100644 --- a/Tranga/TrangaSettings.cs +++ b/Tranga/TrangaSettings.cs @@ -17,6 +17,8 @@ public static class TrangaSettings public static string userAgent { get; private set; } = DefaultUserAgent; public static bool bufferLibraryUpdates { get; private set; } = false; public static bool bufferNotifications { get; private set; } = false; + public static bool compressImages { get; private set; } = true; + public static bool bwImages { get; private set; } = false; [JsonIgnore] public static string settingsFilePath => Path.Join(workingDirectory, "settings.json"); [JsonIgnore] public static string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json"); [JsonIgnore] public static string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json"); @@ -49,7 +51,9 @@ public static class TrangaSettings ExportSettings(); } - public static void CreateOrUpdate(string? downloadDirectory = null, string? pWorkingDirectory = null, int? pApiPortNumber = null, string? pUserAgent = null, bool? pAprilFoolsMode = null, bool? pBufferLibraryUpdates = null, bool? pBufferNotifications = null) + public static void CreateOrUpdate(string? downloadDirectory = null, string? pWorkingDirectory = null, + int? pApiPortNumber = null, string? pUserAgent = null, bool? pAprilFoolsMode = null, + bool? pBufferLibraryUpdates = null, bool? pBufferNotifications = null, bool? pCompressImages = null, bool? pbwImages = null) { if(pWorkingDirectory is null && File.Exists(settingsFilePath)) LoadFromWorkingDirectory(workingDirectory); @@ -60,6 +64,8 @@ public static class TrangaSettings aprilFoolsMode = pAprilFoolsMode ?? aprilFoolsMode; bufferLibraryUpdates = pBufferLibraryUpdates ?? bufferLibraryUpdates; bufferNotifications = pBufferNotifications ?? bufferNotifications; + compressImages = pCompressImages ?? compressImages; + bwImages = pbwImages ?? bwImages; Directory.CreateDirectory(downloadLocation); Directory.CreateDirectory(workingDirectory); ExportSettings(); @@ -99,6 +105,18 @@ public static class TrangaSettings ExportSettings(); } + public static void UpdateCompressImages(bool enabled) + { + compressImages = enabled; + ExportSettings(); + } + + public static void UpdateBwImages(bool enabled) + { + bwImages = enabled; + ExportSettings(); + } + public static void UpdateDownloadLocation(string newPath, bool moveFiles = true) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) @@ -190,6 +208,8 @@ public static class TrangaSettings jobj.Add("requestLimits", JToken.FromObject(requestLimits)); jobj.Add("bufferLibraryUpdates", JToken.FromObject(bufferLibraryUpdates)); jobj.Add("bufferNotifications", JToken.FromObject(bufferNotifications)); + jobj.Add("compressImages", JToken.FromObject(compressImages)); + jobj.Add("bwImages", JToken.FromObject(bwImages)); return jobj; } @@ -214,5 +234,9 @@ public static class TrangaSettings bufferLibraryUpdates = blu.Value()!; if (jobj.TryGetValue("bufferNotifications", out JToken? bn)) bufferNotifications = bn.Value()!; + if (jobj.TryGetValue("compressImages", out JToken? ci)) + compressImages = ci.Value()!; + if (jobj.TryGetValue("bwImages", out JToken? bwi)) + bwImages = bwi.Value()!; } } \ No newline at end of file diff --git a/docs/API_Calls_v2.md b/docs/API_Calls_v2.md index 5f4f77f..34bbce0 100644 --- a/docs/API_Calls_v2.md +++ b/docs/API_Calls_v2.md @@ -728,6 +728,67 @@ Enables/Disables April-Fools-Mode. | 500 | Parsing Error | +### ![GET](https://img.shields.io/badge/GET-0f0) `/v2/Settings/CompressImages` + +Returns the current state of the Image-compression setting. + +
+ Returns + + Boolean +
+ +### ![POST](https://img.shields.io/badge/POST-00f) `/v2/Settings/CompressImages` + +Enables/Disables Imagecompression. + +
+ Request + + | Parameter | Value | + |-----------|------------| + | value | true/false | +
+ +
+ Returns + + | StatusCode | Meaning | + |------------|--------------------------------| + | 500 | Parsing Error | +
+ +### ![GET](https://img.shields.io/badge/GET-0f0) `/v2/Settings/BWImages` + +Returns the current state of the Black/White Image setting. + +
+ Returns + +Boolean +
+ +### ![POST](https://img.shields.io/badge/POST-00f) `/v2/Settings/BWImages` + +Enables/Disables converting Images to Black and White. + +
+ Request + + | Parameter | Value | + |-----------|------------| + | value | true/false | +
+ +
+ Returns + +| StatusCode | Meaning | + |------------|--------------------------------| +| 500 | Parsing Error | +
+ + ### ![POST](https://img.shields.io/badge/POST-00f) `/v2/Settings/DownloadLocation` Updates the default Download-Location. diff --git a/docs/Types.md b/docs/Types.md index 8ca9d05..5543fed 100644 --- a/docs/Types.md +++ b/docs/Types.md @@ -121,6 +121,8 @@ "bufferNotifications": boolean, "version": number, "aprilFoolsMode": boolean, + "compressImages": boolean, + "bwImages": boolean, "requestLimits": { "MangaInfo": number, "MangaDexFeed": number,