diff --git a/Tranga/Chapter.cs b/Tranga/Chapter.cs index 565960c..41d1417 100644 --- a/Tranga/Chapter.cs +++ b/Tranga/Chapter.cs @@ -20,12 +20,13 @@ public readonly struct Chapter : IComparable private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*"); private static readonly Regex IllegalStrings = new(@"Vol(ume)?.?", RegexOptions.IgnoreCase); + private static readonly Regex Digits = new(@"[0-9]*"); public Chapter(Manga parentManga, string? name, string? volumeNumber, string chapterNumber, string url) { this.parentManga = parentManga; this.name = name; - this.volumeNumber = volumeNumber ?? "0"; - this.chapterNumber = chapterNumber; + this.volumeNumber = volumeNumber is not null ? string.Concat(Digits.Matches(volumeNumber).Select(x => x.Value)) : "0"; + this.chapterNumber = string.Concat(Digits.Matches(chapterNumber).Select(x => x.Value)); this.url = url; string chapterVolNumStr; diff --git a/Tranga/MangaConnectors/Bato.cs b/Tranga/MangaConnectors/Bato.cs index ff29f18..96d97c0 100644 --- a/Tranga/MangaConnectors/Bato.cs +++ b/Tranga/MangaConnectors/Bato.cs @@ -7,12 +7,10 @@ namespace Tranga.MangaConnectors; public class Bato : MangaConnector { + public Bato(GlobalBase clone) : base(clone, "Bato") { - this.downloadClient = new HttpDownloadClient(clone, new Dictionary() - { - {1, 60} - }); + this.downloadClient = new HttpDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -21,7 +19,7 @@ public class Bato : MangaConnector string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string requestUrl = $"https://bato.to/v3x-search?word={sanitizedTitle}&lang=en"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -43,8 +41,7 @@ public class Bato : MangaConnector public override Manga? GetMangaFromUrl(string url) { - RequestResult requestResult = - downloadClient.MakeRequest(url, 1); + RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; if (requestResult.htmlDocument is null) @@ -89,7 +86,7 @@ public class Bato : MangaConnector string posterUrl = document.DocumentNode.SelectNodes("//img") .First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&", "&"); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); List genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList(); string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray(); @@ -129,7 +126,7 @@ public class Bato : MangaConnector string requestUrl = $"https://bato.to/title/{manga.publicationId}"; // Leaving this in for verification if the page exists RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -141,7 +138,7 @@ public class Bato : MangaConnector private List ParseChaptersFromHtml(Manga manga, string mangaUrl) { - RequestResult result = downloadClient.MakeRequest(mangaUrl, 1); + RequestResult result = downloadClient.MakeRequest(mangaUrl, RequestType.Default); if ((int)result.statusCode < 200 || (int)result.statusCode >= 300 || result.htmlDocument is null) { Log("Failed to load site"); @@ -184,7 +181,7 @@ public class Bato : MangaConnector string requestUrl = chapter.url; // Leaving this in to check if the page exists RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { progressToken?.Cancel(); @@ -196,13 +193,13 @@ public class Bato : MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken); + return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken); } private string[] ParseImageUrlsFromHtml(string mangaUrl) { RequestResult requestResult = - downloadClient.MakeRequest(mangaUrl, 1); + downloadClient.MakeRequest(mangaUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { return Array.Empty(); diff --git a/Tranga/MangaConnectors/ChromiumDownloadClient.cs b/Tranga/MangaConnectors/ChromiumDownloadClient.cs index cf8f1fd..961d878 100644 --- a/Tranga/MangaConnectors/ChromiumDownloadClient.cs +++ b/Tranga/MangaConnectors/ChromiumDownloadClient.cs @@ -53,7 +53,7 @@ internal class ChromiumDownloadClient : DownloadClient }); } - public ChromiumDownloadClient(GlobalBase clone, Dictionary rateLimitRequestsPerMinute) : base(clone, rateLimitRequestsPerMinute) + public ChromiumDownloadClient(GlobalBase clone) : base(clone) { this.browser = DownloadBrowser().Result; } diff --git a/Tranga/MangaConnectors/DownloadClient.cs b/Tranga/MangaConnectors/DownloadClient.cs index d60ee5f..0e9b343 100644 --- a/Tranga/MangaConnectors/DownloadClient.cs +++ b/Tranga/MangaConnectors/DownloadClient.cs @@ -5,37 +5,29 @@ namespace Tranga.MangaConnectors; internal abstract class DownloadClient : GlobalBase { - private readonly Dictionary _lastExecutedRateLimit; - private readonly Dictionary _rateLimit; + private readonly Dictionary _lastExecutedRateLimit; - protected DownloadClient(GlobalBase clone, Dictionary rateLimitRequestsPerMinute) : base(clone) + protected DownloadClient(GlobalBase clone) : base(clone) { this._lastExecutedRateLimit = new(); - _rateLimit = new(); - foreach (KeyValuePair limit in rateLimitRequestsPerMinute) - _rateLimit.Add(limit.Key, TimeSpan.FromMinutes(1).Divide(limit.Value)); - } - - internal void SetCustomRequestLimit(byte requestType, int limit) - { - if (_rateLimit.ContainsKey(requestType)) - _rateLimit[requestType] = TimeSpan.FromMinutes(1).Divide(limit); - else - _rateLimit.Add(requestType, TimeSpan.FromMinutes(1).Divide(limit)); } - public RequestResult MakeRequest(string url, byte requestType, string? referrer = null, string? clickButton = null) + public RequestResult MakeRequest(string url, RequestType requestType, string? referrer = null, string? clickButton = null) { - if (_rateLimit.TryGetValue(requestType, out TimeSpan value)) - _lastExecutedRateLimit.TryAdd(requestType, DateTime.Now.Subtract(value)); - else + if (!settings.requestLimits.ContainsKey(requestType)) { Log("RequestType not configured for rate-limit."); return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null); } - TimeSpan rateLimitTimeout = _rateLimit[requestType] - .Subtract(DateTime.Now.Subtract(_lastExecutedRateLimit[requestType])); + int rateLimit = settings.userAgent == TrangaSettings.DefaultUserAgent + ? TrangaSettings.DefaultRequestLimits[requestType] + : settings.requestLimits[requestType]; + + TimeSpan timeBetweenRequests = TimeSpan.FromMinutes(1).Divide(rateLimit); + _lastExecutedRateLimit.TryAdd(requestType, DateTime.Now.Subtract(timeBetweenRequests)); + + TimeSpan rateLimitTimeout = timeBetweenRequests.Subtract(DateTime.Now.Subtract(_lastExecutedRateLimit[requestType])); if (rateLimitTimeout > TimeSpan.Zero) { diff --git a/Tranga/MangaConnectors/HttpDownloadClient.cs b/Tranga/MangaConnectors/HttpDownloadClient.cs index 342b8a1..46a0008 100644 --- a/Tranga/MangaConnectors/HttpDownloadClient.cs +++ b/Tranga/MangaConnectors/HttpDownloadClient.cs @@ -11,8 +11,7 @@ internal class HttpDownloadClient : DownloadClient Timeout = TimeSpan.FromSeconds(10) }; - - public HttpDownloadClient(GlobalBase clone, Dictionary rateLimitRequestsPerMinute) : base(clone, rateLimitRequestsPerMinute) + public HttpDownloadClient(GlobalBase clone) : base(clone) { Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", settings.userAgent); } diff --git a/Tranga/MangaConnectors/MangaConnector.cs b/Tranga/MangaConnectors/MangaConnector.cs index 8d7df98..bd80aed 100644 --- a/Tranga/MangaConnectors/MangaConnector.cs +++ b/Tranga/MangaConnectors/MangaConnector.cs @@ -202,7 +202,7 @@ public abstract class MangaConnector : GlobalBase /// /// RequestType for Rate-Limit /// referrer used in html request header - private HttpStatusCode DownloadImage(string imageUrl, string fullPath, byte requestType, string? referrer = null) + private HttpStatusCode DownloadImage(string imageUrl, string fullPath, RequestType requestType, string? referrer = null) { RequestResult requestResult = downloadClient.MakeRequest(imageUrl, requestType, referrer); @@ -217,7 +217,7 @@ public abstract class MangaConnector : GlobalBase return requestResult.statusCode; } - protected HttpStatusCode DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, byte requestType, string? comicInfoPath = null, string? referrer = null, ProgressToken? progressToken = null) + protected HttpStatusCode DownloadChapterImages(string[] imageUrls, string saveArchiveFilePath, RequestType requestType, string? comicInfoPath = null, string? referrer = null, ProgressToken? progressToken = null) { if (progressToken?.cancellationRequested ?? false) return HttpStatusCode.RequestTimeout; @@ -274,7 +274,7 @@ public abstract class MangaConnector : GlobalBase return HttpStatusCode.OK; } - protected string SaveCoverImageToCache(string url, byte requestType) + protected string SaveCoverImageToCache(string url, RequestType requestType) { string filetype = url.Split('/')[^1].Split('?')[0].Split('.')[^1]; string filename = $"{DateTime.Now.Ticks.ToString()}.{filetype}"; diff --git a/Tranga/MangaConnectors/MangaDex.cs b/Tranga/MangaConnectors/MangaDex.cs index eb12f8b..08c4bac 100644 --- a/Tranga/MangaConnectors/MangaDex.cs +++ b/Tranga/MangaConnectors/MangaDex.cs @@ -7,25 +7,9 @@ using JsonSerializer = System.Text.Json.JsonSerializer; namespace Tranga.MangaConnectors; public class MangaDex : MangaConnector { - private enum RequestType : byte - { - Manga, - Feed, - AtHomeServer, - CoverUrl, - Author, - } - public MangaDex(GlobalBase clone) : base(clone, "MangaDex") { - this.downloadClient = new HttpDownloadClient(clone, new Dictionary() - { - {(byte)RequestType.Manga, 250}, - {(byte)RequestType.Feed, 250}, - {(byte)RequestType.AtHomeServer, 40}, - {(byte)RequestType.CoverUrl, 250}, - {(byte)RequestType.Author, 250} - }); + this.downloadClient = new HttpDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -41,7 +25,7 @@ public class MangaDex : MangaConnector //Request next Page RequestResult requestResult = downloadClient.MakeRequest( - $"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}", (byte)RequestType.Manga); + $"https://api.mangadex.org/manga?limit={limit}&title={publicationTitle}&offset={offset}", RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) break; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -75,7 +59,7 @@ public class MangaDex : MangaConnector public override Manga? GetMangaFromId(string publicationId) { RequestResult requestResult = - downloadClient.MakeRequest($"https://api.mangadex.org/manga/{publicationId}", (byte)RequestType.Manga); + downloadClient.MakeRequest($"https://api.mangadex.org/manga/{publicationId}", RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -149,7 +133,7 @@ public class MangaDex : MangaConnector string? coverUrl = GetCoverUrl(publicationId, posterId); string? coverCacheName = null; if (coverUrl is not null) - coverCacheName = SaveCoverImageToCache(coverUrl, (byte)RequestType.AtHomeServer); + coverCacheName = SaveCoverImageToCache(coverUrl, RequestType.MangaCover); List authors = GetAuthors(authorIds); @@ -216,7 +200,7 @@ public class MangaDex : MangaConnector //Request next "Page" RequestResult requestResult = downloadClient.MakeRequest( - $"https://api.mangadex.org/manga/{manga.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", (byte)RequestType.Feed); + $"https://api.mangadex.org/manga/{manga.publicationId}/feed?limit={limit}&offset={offset}&translatedLanguage%5B%5D={language}", RequestType.MangaDexFeed); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) break; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -268,7 +252,7 @@ public class MangaDex : MangaConnector Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); //Request URLs for Chapter-Images RequestResult requestResult = - downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", (byte)RequestType.AtHomeServer); + downloadClient.MakeRequest($"https://api.mangadex.org/at-home/server/{chapter.url}?forcePort443=false'", RequestType.MangaDexImage); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { progressToken?.Cancel(); @@ -293,7 +277,7 @@ public class MangaDex : MangaConnector File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); //Download Chapter-Images - return DownloadChapterImages(imageUrls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), (byte)RequestType.AtHomeServer, comicInfoPath, progressToken:progressToken); + return DownloadChapterImages(imageUrls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, progressToken:progressToken); } private string? GetCoverUrl(string publicationId, string? posterId) @@ -307,7 +291,7 @@ public class MangaDex : MangaConnector //Request information where to download Cover RequestResult requestResult = - downloadClient.MakeRequest($"https://api.mangadex.org/cover/{posterId}", (byte)RequestType.CoverUrl); + downloadClient.MakeRequest($"https://api.mangadex.org/cover/{posterId}", RequestType.MangaCover); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); @@ -328,7 +312,7 @@ public class MangaDex : MangaConnector foreach (string authorId in authorIds) { RequestResult requestResult = - downloadClient.MakeRequest($"https://api.mangadex.org/author/{authorId}", (byte)RequestType.Author); + downloadClient.MakeRequest($"https://api.mangadex.org/author/{authorId}", RequestType.MangaDexAuthor); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return ret; JsonObject? result = JsonSerializer.Deserialize(requestResult.result); diff --git a/Tranga/MangaConnectors/MangaKatana.cs b/Tranga/MangaConnectors/MangaKatana.cs index 9703934..a62315b 100644 --- a/Tranga/MangaConnectors/MangaKatana.cs +++ b/Tranga/MangaConnectors/MangaKatana.cs @@ -9,10 +9,7 @@ public class MangaKatana : MangaConnector { public MangaKatana(GlobalBase clone) : base(clone, "MangaKatana") { - this.downloadClient = new HttpDownloadClient(clone, new Dictionary() - { - {1, 60} - }); + this.downloadClient = new HttpDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -21,7 +18,7 @@ public class MangaKatana : MangaConnector string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower(); string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -47,7 +44,7 @@ public class MangaKatana : MangaConnector public override Manga? GetMangaFromUrl(string url) { RequestResult requestResult = - downloadClient.MakeRequest(url, 1); + downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1]); @@ -131,7 +128,7 @@ public class MangaKatana : MangaConnector string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First() .GetAttributes().First(a => a.Name == "src").Value; - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText; while (description.StartsWith('\n')) @@ -158,7 +155,7 @@ public class MangaKatana : MangaConnector string requestUrl = $"https://mangakatana.com/manga/{manga.publicationId}"; // Leaving this in for verification if the page exists RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -210,7 +207,7 @@ public class MangaKatana : MangaConnector string requestUrl = chapter.url; // Leaving this in to check if the page exists RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { progressToken?.Cancel(); @@ -222,7 +219,7 @@ public class MangaKatana : MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken); + return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, "https://mangakatana.com/", progressToken:progressToken); } private string[] ParseImageUrlsFromHtml(string mangaUrl) diff --git a/Tranga/MangaConnectors/MangaLife.cs b/Tranga/MangaConnectors/MangaLife.cs index d50b6ef..fd92377 100644 --- a/Tranga/MangaConnectors/MangaLife.cs +++ b/Tranga/MangaConnectors/MangaLife.cs @@ -9,10 +9,7 @@ public class MangaLife : MangaConnector { public MangaLife(GlobalBase clone) : base(clone, "Manga4Life") { - this.downloadClient = new ChromiumDownloadClient(clone, new Dictionary() - { - { 1, 60 } - }); + this.downloadClient = new ChromiumDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -21,7 +18,7 @@ public class MangaLife : MangaConnector string sanitizedTitle = WebUtility.UrlEncode(publicationTitle); string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -42,7 +39,7 @@ public class MangaLife : MangaConnector Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*"); string publicationId = publicationIdRex.Match(url).Groups[2].Value; - RequestResult requestResult = this.downloadClient.MakeRequest(url, 1); + RequestResult requestResult = this.downloadClient.MakeRequest(url, RequestType.MangaInfo); if(requestResult.htmlDocument is not null) return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); return null; @@ -81,7 +78,7 @@ public class MangaLife : MangaConnector HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); string posterUrl = posterNode.GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1"); string sortName = titleNode.InnerText; @@ -133,7 +130,7 @@ public class MangaLife : MangaConnector public override Chapter[] GetChapters(Manga manga, string language="en") { Log($"Getting chapters {manga}"); - RequestResult result = downloadClient.MakeRequest($"https://manga4life.com/manga/{manga.publicationId}", 1, clickButton:"[class*='ShowAllChapters']"); + RequestResult result = downloadClient.MakeRequest($"https://manga4life.com/manga/{manga.publicationId}", RequestType.Default, clickButton:"[class*='ShowAllChapters']"); if ((int)result.statusCode < 200 || (int)result.statusCode >= 300 || result.htmlDocument is null) { return Array.Empty(); @@ -179,7 +176,7 @@ public class MangaLife : MangaConnector Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); - RequestResult requestResult = this.downloadClient.MakeRequest(chapter.url, 1); + RequestResult requestResult = this.downloadClient.MakeRequest(chapter.url, RequestType.Default); if (requestResult.htmlDocument is null) { progressToken?.Cancel(); @@ -197,6 +194,6 @@ public class MangaLife : MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, progressToken:progressToken); + return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, progressToken:progressToken); } } \ No newline at end of file diff --git a/Tranga/MangaConnectors/Manganato.cs b/Tranga/MangaConnectors/Manganato.cs index ff301b9..d7bbd79 100644 --- a/Tranga/MangaConnectors/Manganato.cs +++ b/Tranga/MangaConnectors/Manganato.cs @@ -9,10 +9,7 @@ public class Manganato : MangaConnector { public Manganato(GlobalBase clone) : base(clone, "Manganato") { - this.downloadClient = new HttpDownloadClient(clone, new Dictionary() - { - {1, 60} - }); + this.downloadClient = new HttpDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -21,7 +18,7 @@ public class Manganato : MangaConnector string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -62,7 +59,7 @@ public class Manganato : MangaConnector public override Manga? GetMangaFromUrl(string url) { RequestResult requestResult = - downloadClient.MakeRequest(url, 1); + downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; @@ -121,7 +118,7 @@ public class Manganato : MangaConnector string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First() .GetAttributes().First(a => a.Name == "src").Value; - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description")) .InnerText.Replace("Description :", ""); @@ -143,7 +140,7 @@ public class Manganato : MangaConnector Log($"Getting chapters {manga}"); string requestUrl = $"https://chapmanganato.com/{manga.publicationId}"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -192,7 +189,7 @@ public class Manganato : MangaConnector Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); string requestUrl = chapter.url; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { progressToken?.Cancel(); @@ -210,7 +207,7 @@ public class Manganato : MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://chapmanganato.com/", progressToken:progressToken); + return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, "https://chapmanganato.com/", progressToken:progressToken); } private string[] ParseImageUrlsFromHtml(HtmlDocument document) diff --git a/Tranga/MangaConnectors/Mangasee.cs b/Tranga/MangaConnectors/Mangasee.cs index 37d5060..c778793 100644 --- a/Tranga/MangaConnectors/Mangasee.cs +++ b/Tranga/MangaConnectors/Mangasee.cs @@ -12,10 +12,7 @@ public class Mangasee : MangaConnector { public Mangasee(GlobalBase clone) : base(clone, "Mangasee") { - this.downloadClient = new ChromiumDownloadClient(clone, new Dictionary() - { - { 1, 60 } - }); + this.downloadClient = new ChromiumDownloadClient(clone); } private struct SearchResult @@ -30,7 +27,7 @@ public class Mangasee : MangaConnector Log($"Searching Publications. Term=\"{publicationTitle}\""); string requestUrl = "https://mangasee123.com/_search.php"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { Log($"Failed to retrieve search: {requestResult.statusCode}"); @@ -121,7 +118,7 @@ public class Mangasee : MangaConnector Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*"); string publicationId = publicationIdRex.Match(url).Groups[1].Value; - RequestResult requestResult = this.downloadClient.MakeRequest(url, 1); + RequestResult requestResult = this.downloadClient.MakeRequest(url, RequestType.MangaInfo); if((int)requestResult.statusCode < 300 && (int)requestResult.statusCode >= 200 && requestResult.htmlDocument is not null) return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); return null; @@ -136,7 +133,7 @@ public class Mangasee : MangaConnector HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); string posterUrl = posterNode.GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1"); string sortName = titleNode.InnerText; @@ -232,7 +229,7 @@ public class Mangasee : MangaConnector Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); - RequestResult requestResult = this.downloadClient.MakeRequest(chapter.url, 1); + RequestResult requestResult = this.downloadClient.MakeRequest(chapter.url, RequestType.Default); if (requestResult.htmlDocument is null) { progressToken?.Cancel(); @@ -250,6 +247,6 @@ public class Mangasee : MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, progressToken:progressToken); + return DownloadChapterImages(urls.ToArray(), chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, progressToken:progressToken); } } \ No newline at end of file diff --git a/Tranga/MangaConnectors/Mangaworld.cs b/Tranga/MangaConnectors/Mangaworld.cs index 8f41e8b..a457999 100644 --- a/Tranga/MangaConnectors/Mangaworld.cs +++ b/Tranga/MangaConnectors/Mangaworld.cs @@ -9,10 +9,7 @@ public class Mangaworld: MangaConnector { public Mangaworld(GlobalBase clone) : base(clone, "Mangaworld") { - this.downloadClient = new HttpDownloadClient(clone, new Dictionary() - { - {1, 60} - }); + this.downloadClient = new HttpDownloadClient(clone); } public override Manga[] GetManga(string publicationTitle = "") @@ -21,7 +18,7 @@ public class Mangaworld: MangaConnector string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower(); string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -62,7 +59,7 @@ public class Mangaworld: MangaConnector public override Manga? GetMangaFromUrl(string url) { RequestResult requestResult = - downloadClient.MakeRequest(url, 1); + downloadClient.MakeRequest(url, RequestType.MangaInfo); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return null; @@ -114,7 +111,7 @@ public class Mangaworld: MangaConnector string posterUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", ""); - string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); + string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover); string description = document.DocumentNode.SelectSingleNode("//div[@id='noidungm']").InnerText; @@ -132,7 +129,7 @@ public class Mangaworld: MangaConnector Log($"Getting chapters {manga}"); string requestUrl = $"https://www.mangaworld.ac/manga/{manga.publicationId}"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) return Array.Empty(); @@ -191,7 +188,7 @@ public class Mangaworld: MangaConnector Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); string requestUrl = $"{chapter.url}?style=list"; RequestResult requestResult = - downloadClient.MakeRequest(requestUrl, 1); + downloadClient.MakeRequest(requestUrl, RequestType.Default); if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) { progressToken?.Cancel(); @@ -209,7 +206,7 @@ public class Mangaworld: MangaConnector string comicInfoPath = Path.GetTempFileName(); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); - return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), 1, comicInfoPath, "https://www.mangaworld.bz/", progressToken:progressToken); + return DownloadChapterImages(imageUrls, chapter.GetArchiveFilePath(settings.downloadLocation), RequestType.MangaImage, comicInfoPath, "https://www.mangaworld.bz/", progressToken:progressToken); } private string[] ParseImageUrlsFromHtml(HtmlDocument document) diff --git a/Tranga/MangaConnectors/RequestType.cs b/Tranga/MangaConnectors/RequestType.cs new file mode 100644 index 0000000..eb89b42 --- /dev/null +++ b/Tranga/MangaConnectors/RequestType.cs @@ -0,0 +1,12 @@ +namespace Tranga.MangaConnectors; + +public enum RequestType : byte +{ + Default = 0, + MangaDexFeed = 1, + MangaImage = 2, + MangaCover = 3, + MangaDexAuthor = 4, + MangaDexImage = 5, + MangaInfo = 6 +} \ No newline at end of file diff --git a/Tranga/Server.cs b/Tranga/Server.cs index 6aa518f..c12f1fa 100644 --- a/Tranga/Server.cs +++ b/Tranga/Server.cs @@ -399,16 +399,19 @@ public class Server : GlobalBase case "Settings/customRequestLimit": if (!requestVariables.TryGetValue("requestType", out string? requestTypeStr) || !requestVariables.TryGetValue("requestsPerMinute", out string? requestsPerMinuteStr) || - !requestVariables.TryGetValue("connector", out connectorName) || - !byte.TryParse(requestTypeStr, out byte requestType) || - !int.TryParse(requestsPerMinuteStr, out int requestsPerMinute) || - !_parent.TryGetConnector(connectorName, out connector)) + !Enum.TryParse(requestTypeStr, out RequestType requestType) || + !int.TryParse(requestsPerMinuteStr, out int requestsPerMinute)) { SendResponse(HttpStatusCode.BadRequest, response); break; } - connector!.downloadClient.SetCustomRequestLimit(requestType, requestsPerMinute); - SendResponse(HttpStatusCode.Accepted, response); + + if (settings.requestLimits.ContainsKey(requestType)) + { + settings.requestLimits[requestType] = requestsPerMinute; + SendResponse(HttpStatusCode.Accepted, response); + }else + SendResponse(HttpStatusCode.BadRequest, response); break; case "NotificationConnectors/Update": if (!requestVariables.TryGetValue("notificationConnector", out string? notificationConnectorStr) || diff --git a/Tranga/TrangaSettings.cs b/Tranga/TrangaSettings.cs index a75c8dd..4058188 100644 --- a/Tranga/TrangaSettings.cs +++ b/Tranga/TrangaSettings.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using Newtonsoft.Json; using Tranga.LibraryConnectors; +using Tranga.MangaConnectors; using Tranga.NotificationConnectors; using static System.IO.UnixFileMode; @@ -13,15 +14,26 @@ public class TrangaSettings public string workingDirectory { get; private set; } public int apiPortNumber { get; init; } public string styleSheet { get; private set; } - - public string userAgent { get; set; } = - $"Tranga ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")}) / 1.0"; + public string userAgent { get; set; } = DefaultUserAgent; [JsonIgnore] public string settingsFilePath => Path.Join(workingDirectory, "settings.json"); [JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json"); [JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json"); [JsonIgnore] public string jobsFolderPath => Path.Join(workingDirectory, "jobs"); [JsonIgnore] public string coverImageCache => Path.Join(workingDirectory, "imageCache"); - public ushort? version { get; set; } + [JsonIgnore] internal static readonly string DefaultUserAgent = $"Tranga ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")}) / 1.0"; + public ushort? version { get; set; } = 1; + [JsonIgnore]internal static readonly Dictionary DefaultRequestLimits = new () + { + {RequestType.MangaInfo, 250}, + {RequestType.MangaDexFeed, 250}, + {RequestType.MangaDexImage, 40}, + {RequestType.MangaImage, 60}, + {RequestType.MangaCover, 250}, + {RequestType.MangaDexAuthor, 250}, + {RequestType.Default, 60} + }; + + public Dictionary requestLimits { get; set; } = DefaultRequestLimits; public TrangaSettings(string? downloadLocation = null, string? workingDirectory = null, int? apiPortNumber = null) {