2
0

#109 Rate Limits

Moved Config for RateLimits to TrangaSettings
Updated API: Settings/customRequestLimit
requestType in RequestType.cs
requestsPerMinute as int
This commit is contained in:
Glax 2024-02-11 00:35:33 +01:00
parent c008d55f26
commit 0b4461265c
15 changed files with 110 additions and 125 deletions

View File

@ -20,12 +20,13 @@ public readonly struct Chapter : IComparable
private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*"); private static readonly Regex LegalCharacters = new (@"([A-z]*[0-9]* *\.*-*,*\]*\[*'*\'*\)*\(*~*!*)*");
private static readonly Regex IllegalStrings = new(@"Vol(ume)?.?", RegexOptions.IgnoreCase); 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) public Chapter(Manga parentManga, string? name, string? volumeNumber, string chapterNumber, string url)
{ {
this.parentManga = parentManga; this.parentManga = parentManga;
this.name = name; this.name = name;
this.volumeNumber = volumeNumber ?? "0"; this.volumeNumber = volumeNumber is not null ? string.Concat(Digits.Matches(volumeNumber).Select(x => x.Value)) : "0";
this.chapterNumber = chapterNumber; this.chapterNumber = string.Concat(Digits.Matches(chapterNumber).Select(x => x.Value));
this.url = url; this.url = url;
string chapterVolNumStr; string chapterVolNumStr;

View File

@ -7,12 +7,10 @@ namespace Tranga.MangaConnectors;
public class Bato : MangaConnector public class Bato : MangaConnector
{ {
public Bato(GlobalBase clone) : base(clone, "Bato") public Bato(GlobalBase clone) : base(clone, "Bato")
{ {
this.downloadClient = new HttpDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new HttpDownloadClient(clone);
{
{1, 60}
});
} }
public override Manga[] GetManga(string publicationTitle = "") 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 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"; string requestUrl = $"https://bato.to/v3x-search?word={sanitizedTitle}&lang=en";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Manga>();
@ -43,8 +41,7 @@ public class Bato : MangaConnector
public override Manga? GetMangaFromUrl(string url) public override Manga? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo);
downloadClient.MakeRequest(url, 1);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; return null;
if (requestResult.htmlDocument is null) if (requestResult.htmlDocument is null)
@ -89,7 +86,7 @@ public class Bato : MangaConnector
string posterUrl = document.DocumentNode.SelectNodes("//img") string posterUrl = document.DocumentNode.SelectNodes("//img")
.First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&amp;", "&"); .First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&amp;", "&");
string coverFileNameInCache = SaveCoverImageToCache(posterUrl, 1); string coverFileNameInCache = SaveCoverImageToCache(posterUrl, RequestType.MangaCover);
List<HtmlNode> genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList(); List<HtmlNode> genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList();
string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray(); 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}"; string requestUrl = $"https://bato.to/title/{manga.publicationId}";
// Leaving this in for verification if the page exists // Leaving this in for verification if the page exists
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Chapter>(); return Array.Empty<Chapter>();
@ -141,7 +138,7 @@ public class Bato : MangaConnector
private List<Chapter> ParseChaptersFromHtml(Manga manga, string mangaUrl) private List<Chapter> 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) if ((int)result.statusCode < 200 || (int)result.statusCode >= 300 || result.htmlDocument is null)
{ {
Log("Failed to load site"); Log("Failed to load site");
@ -184,7 +181,7 @@ public class Bato : MangaConnector
string requestUrl = chapter.url; string requestUrl = chapter.url;
// Leaving this in to check if the page exists // Leaving this in to check if the page exists
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -196,13 +193,13 @@ public class Bato : MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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) private string[] ParseImageUrlsFromHtml(string mangaUrl)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(mangaUrl, 1); downloadClient.MakeRequest(mangaUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
return Array.Empty<string>(); return Array.Empty<string>();

View File

@ -53,7 +53,7 @@ internal class ChromiumDownloadClient : DownloadClient
}); });
} }
public ChromiumDownloadClient(GlobalBase clone, Dictionary<byte, int> rateLimitRequestsPerMinute) : base(clone, rateLimitRequestsPerMinute) public ChromiumDownloadClient(GlobalBase clone) : base(clone)
{ {
this.browser = DownloadBrowser().Result; this.browser = DownloadBrowser().Result;
} }

View File

@ -5,37 +5,29 @@ namespace Tranga.MangaConnectors;
internal abstract class DownloadClient : GlobalBase internal abstract class DownloadClient : GlobalBase
{ {
private readonly Dictionary<byte, DateTime> _lastExecutedRateLimit; private readonly Dictionary<RequestType, DateTime> _lastExecutedRateLimit;
private readonly Dictionary<byte, TimeSpan> _rateLimit;
protected DownloadClient(GlobalBase clone, Dictionary<byte, int> rateLimitRequestsPerMinute) : base(clone) protected DownloadClient(GlobalBase clone) : base(clone)
{ {
this._lastExecutedRateLimit = new(); this._lastExecutedRateLimit = new();
_rateLimit = new();
foreach (KeyValuePair<byte, int> 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)) if (!settings.requestLimits.ContainsKey(requestType))
_lastExecutedRateLimit.TryAdd(requestType, DateTime.Now.Subtract(value));
else
{ {
Log("RequestType not configured for rate-limit."); Log("RequestType not configured for rate-limit.");
return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null); return new RequestResult(HttpStatusCode.NotAcceptable, null, Stream.Null);
} }
TimeSpan rateLimitTimeout = _rateLimit[requestType] int rateLimit = settings.userAgent == TrangaSettings.DefaultUserAgent
.Subtract(DateTime.Now.Subtract(_lastExecutedRateLimit[requestType])); ? 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) if (rateLimitTimeout > TimeSpan.Zero)
{ {

View File

@ -11,8 +11,7 @@ internal class HttpDownloadClient : DownloadClient
Timeout = TimeSpan.FromSeconds(10) Timeout = TimeSpan.FromSeconds(10)
}; };
public HttpDownloadClient(GlobalBase clone) : base(clone)
public HttpDownloadClient(GlobalBase clone, Dictionary<byte, int> rateLimitRequestsPerMinute) : base(clone, rateLimitRequestsPerMinute)
{ {
Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", settings.userAgent); Client.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", settings.userAgent);
} }

View File

@ -202,7 +202,7 @@ public abstract class MangaConnector : GlobalBase
/// <param name="fullPath"></param> /// <param name="fullPath"></param>
/// <param name="requestType">RequestType for Rate-Limit</param> /// <param name="requestType">RequestType for Rate-Limit</param>
/// <param name="referrer">referrer used in html request header</param> /// <param name="referrer">referrer used in html request header</param>
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); RequestResult requestResult = downloadClient.MakeRequest(imageUrl, requestType, referrer);
@ -217,7 +217,7 @@ public abstract class MangaConnector : GlobalBase
return requestResult.statusCode; 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) if (progressToken?.cancellationRequested ?? false)
return HttpStatusCode.RequestTimeout; return HttpStatusCode.RequestTimeout;
@ -274,7 +274,7 @@ public abstract class MangaConnector : GlobalBase
return HttpStatusCode.OK; 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 filetype = url.Split('/')[^1].Split('?')[0].Split('.')[^1];
string filename = $"{DateTime.Now.Ticks.ToString()}.{filetype}"; string filename = $"{DateTime.Now.Ticks.ToString()}.{filetype}";

View File

@ -7,25 +7,9 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
namespace Tranga.MangaConnectors; namespace Tranga.MangaConnectors;
public class MangaDex : MangaConnector public class MangaDex : MangaConnector
{ {
private enum RequestType : byte
{
Manga,
Feed,
AtHomeServer,
CoverUrl,
Author,
}
public MangaDex(GlobalBase clone) : base(clone, "MangaDex") public MangaDex(GlobalBase clone) : base(clone, "MangaDex")
{ {
this.downloadClient = new HttpDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new HttpDownloadClient(clone);
{
{(byte)RequestType.Manga, 250},
{(byte)RequestType.Feed, 250},
{(byte)RequestType.AtHomeServer, 40},
{(byte)RequestType.CoverUrl, 250},
{(byte)RequestType.Author, 250}
});
} }
public override Manga[] GetManga(string publicationTitle = "") public override Manga[] GetManga(string publicationTitle = "")
@ -41,7 +25,7 @@ public class MangaDex : MangaConnector
//Request next Page //Request next Page
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest( 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
break; break;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -75,7 +59,7 @@ public class MangaDex : MangaConnector
public override Manga? GetMangaFromId(string publicationId) public override Manga? GetMangaFromId(string publicationId)
{ {
RequestResult requestResult = 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; return null;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -149,7 +133,7 @@ public class MangaDex : MangaConnector
string? coverUrl = GetCoverUrl(publicationId, posterId); string? coverUrl = GetCoverUrl(publicationId, posterId);
string? coverCacheName = null; string? coverCacheName = null;
if (coverUrl is not null) if (coverUrl is not null)
coverCacheName = SaveCoverImageToCache(coverUrl, (byte)RequestType.AtHomeServer); coverCacheName = SaveCoverImageToCache(coverUrl, RequestType.MangaCover);
List<string> authors = GetAuthors(authorIds); List<string> authors = GetAuthors(authorIds);
@ -216,7 +200,7 @@ public class MangaDex : MangaConnector
//Request next "Page" //Request next "Page"
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest( 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
break; break;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -268,7 +252,7 @@ public class MangaDex : MangaConnector
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
//Request URLs for Chapter-Images //Request URLs for Chapter-Images
RequestResult requestResult = 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -293,7 +277,7 @@ public class MangaDex : MangaConnector
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString());
//Download Chapter-Images //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) private string? GetCoverUrl(string publicationId, string? posterId)
@ -307,7 +291,7 @@ public class MangaDex : MangaConnector
//Request information where to download Cover //Request information where to download Cover
RequestResult requestResult = 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; return null;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);
@ -328,7 +312,7 @@ public class MangaDex : MangaConnector
foreach (string authorId in authorIds) foreach (string authorId in authorIds)
{ {
RequestResult requestResult = 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) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return ret; return ret;
JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result); JsonObject? result = JsonSerializer.Deserialize<JsonObject>(requestResult.result);

View File

@ -9,10 +9,7 @@ public class MangaKatana : MangaConnector
{ {
public MangaKatana(GlobalBase clone) : base(clone, "MangaKatana") public MangaKatana(GlobalBase clone) : base(clone, "MangaKatana")
{ {
this.downloadClient = new HttpDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new HttpDownloadClient(clone);
{
{1, 60}
});
} }
public override Manga[] GetManga(string publicationTitle = "") 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 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"; string requestUrl = $"https://mangakatana.com/?search={sanitizedTitle}&search_by=book_name";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Manga>();
@ -47,7 +44,7 @@ public class MangaKatana : MangaConnector
public override Manga? GetMangaFromUrl(string url) public override Manga? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, 1); downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; return null;
return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1]); 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() string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First()
.GetAttributes().First(a => a.Name == "src").Value; .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; string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText;
while (description.StartsWith('\n')) while (description.StartsWith('\n'))
@ -158,7 +155,7 @@ public class MangaKatana : MangaConnector
string requestUrl = $"https://mangakatana.com/manga/{manga.publicationId}"; string requestUrl = $"https://mangakatana.com/manga/{manga.publicationId}";
// Leaving this in for verification if the page exists // Leaving this in for verification if the page exists
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Chapter>(); return Array.Empty<Chapter>();
@ -210,7 +207,7 @@ public class MangaKatana : MangaConnector
string requestUrl = chapter.url; string requestUrl = chapter.url;
// Leaving this in to check if the page exists // Leaving this in to check if the page exists
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -222,7 +219,7 @@ public class MangaKatana : MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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) private string[] ParseImageUrlsFromHtml(string mangaUrl)

View File

@ -9,10 +9,7 @@ public class MangaLife : MangaConnector
{ {
public MangaLife(GlobalBase clone) : base(clone, "Manga4Life") public MangaLife(GlobalBase clone) : base(clone, "Manga4Life")
{ {
this.downloadClient = new ChromiumDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new ChromiumDownloadClient(clone);
{
{ 1, 60 }
});
} }
public override Manga[] GetManga(string publicationTitle = "") public override Manga[] GetManga(string publicationTitle = "")
@ -21,7 +18,7 @@ public class MangaLife : MangaConnector
string sanitizedTitle = WebUtility.UrlEncode(publicationTitle); string sanitizedTitle = WebUtility.UrlEncode(publicationTitle);
string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}"; string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Manga>();
@ -42,7 +39,7 @@ public class MangaLife : MangaConnector
Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*"); Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[2].Value; 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) if(requestResult.htmlDocument is not null)
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId);
return null; return null;
@ -81,7 +78,7 @@ public class MangaLife : MangaConnector
HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img");
string posterUrl = posterNode.GetAttributeValue("src", ""); 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"); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1");
string sortName = titleNode.InnerText; string sortName = titleNode.InnerText;
@ -133,7 +130,7 @@ public class MangaLife : MangaConnector
public override Chapter[] GetChapters(Manga manga, string language="en") public override Chapter[] GetChapters(Manga manga, string language="en")
{ {
Log($"Getting chapters {manga}"); 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) if ((int)result.statusCode < 200 || (int)result.statusCode >= 300 || result.htmlDocument is null)
{ {
return Array.Empty<Chapter>(); return Array.Empty<Chapter>();
@ -179,7 +176,7 @@ public class MangaLife : MangaConnector
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); 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) if (requestResult.htmlDocument is null)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -197,6 +194,6 @@ public class MangaLife : MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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);
} }
} }

View File

@ -9,10 +9,7 @@ public class Manganato : MangaConnector
{ {
public Manganato(GlobalBase clone) : base(clone, "Manganato") public Manganato(GlobalBase clone) : base(clone, "Manganato")
{ {
this.downloadClient = new HttpDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new HttpDownloadClient(clone);
{
{1, 60}
});
} }
public override Manga[] GetManga(string publicationTitle = "") 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 sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}"; string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Manga>();
@ -62,7 +59,7 @@ public class Manganato : MangaConnector
public override Manga? GetMangaFromUrl(string url) public override Manga? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, 1); downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; 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() string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
.GetAttributes().First(a => a.Name == "src").Value; .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")) string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description"))
.InnerText.Replace("Description :", ""); .InnerText.Replace("Description :", "");
@ -143,7 +140,7 @@ public class Manganato : MangaConnector
Log($"Getting chapters {manga}"); Log($"Getting chapters {manga}");
string requestUrl = $"https://chapmanganato.com/{manga.publicationId}"; string requestUrl = $"https://chapmanganato.com/{manga.publicationId}";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Chapter>(); return Array.Empty<Chapter>();
@ -192,7 +189,7 @@ public class Manganato : MangaConnector
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
string requestUrl = chapter.url; string requestUrl = chapter.url;
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -210,7 +207,7 @@ public class Manganato : MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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) private string[] ParseImageUrlsFromHtml(HtmlDocument document)

View File

@ -12,10 +12,7 @@ public class Mangasee : MangaConnector
{ {
public Mangasee(GlobalBase clone) : base(clone, "Mangasee") public Mangasee(GlobalBase clone) : base(clone, "Mangasee")
{ {
this.downloadClient = new ChromiumDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new ChromiumDownloadClient(clone);
{
{ 1, 60 }
});
} }
private struct SearchResult private struct SearchResult
@ -30,7 +27,7 @@ public class Mangasee : MangaConnector
Log($"Searching Publications. Term=\"{publicationTitle}\""); Log($"Searching Publications. Term=\"{publicationTitle}\"");
string requestUrl = "https://mangasee123.com/_search.php"; string requestUrl = "https://mangasee123.com/_search.php";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
Log($"Failed to retrieve search: {requestResult.statusCode}"); Log($"Failed to retrieve search: {requestResult.statusCode}");
@ -121,7 +118,7 @@ public class Mangasee : MangaConnector
Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*"); Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[1].Value; 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) if((int)requestResult.statusCode < 300 && (int)requestResult.statusCode >= 200 && requestResult.htmlDocument is not null)
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId); return ParseSinglePublicationFromHtml(requestResult.htmlDocument, publicationId);
return null; return null;
@ -136,7 +133,7 @@ public class Mangasee : MangaConnector
HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img"); HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img");
string posterUrl = posterNode.GetAttributeValue("src", ""); 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"); HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1");
string sortName = titleNode.InnerText; string sortName = titleNode.InnerText;
@ -232,7 +229,7 @@ public class Mangasee : MangaConnector
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); 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) if (requestResult.htmlDocument is null)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -250,6 +247,6 @@ public class Mangasee : MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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);
} }
} }

View File

@ -9,10 +9,7 @@ public class Mangaworld: MangaConnector
{ {
public Mangaworld(GlobalBase clone) : base(clone, "Mangaworld") public Mangaworld(GlobalBase clone) : base(clone, "Mangaworld")
{ {
this.downloadClient = new HttpDownloadClient(clone, new Dictionary<byte, int>() this.downloadClient = new HttpDownloadClient(clone);
{
{1, 60}
});
} }
public override Manga[] GetManga(string publicationTitle = "") 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 sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}"; string requestUrl = $"https://www.mangaworld.ac/archive?keyword={sanitizedTitle}";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>(); return Array.Empty<Manga>();
@ -62,7 +59,7 @@ public class Mangaworld: MangaConnector
public override Manga? GetMangaFromUrl(string url) public override Manga? GetMangaFromUrl(string url)
{ {
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(url, 1); downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return null; return null;
@ -114,7 +111,7 @@ public class Mangaworld: MangaConnector
string posterUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", ""); 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; string description = document.DocumentNode.SelectSingleNode("//div[@id='noidungm']").InnerText;
@ -132,7 +129,7 @@ public class Mangaworld: MangaConnector
Log($"Getting chapters {manga}"); Log($"Getting chapters {manga}");
string requestUrl = $"https://www.mangaworld.ac/manga/{manga.publicationId}"; string requestUrl = $"https://www.mangaworld.ac/manga/{manga.publicationId}";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Chapter>(); return Array.Empty<Chapter>();
@ -191,7 +188,7 @@ public class Mangaworld: MangaConnector
Log($"Retrieving chapter-info {chapter} {chapterParentManga}"); Log($"Retrieving chapter-info {chapter} {chapterParentManga}");
string requestUrl = $"{chapter.url}?style=list"; string requestUrl = $"{chapter.url}?style=list";
RequestResult requestResult = RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, 1); downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300) if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
{ {
progressToken?.Cancel(); progressToken?.Cancel();
@ -209,7 +206,7 @@ public class Mangaworld: MangaConnector
string comicInfoPath = Path.GetTempFileName(); string comicInfoPath = Path.GetTempFileName();
File.WriteAllText(comicInfoPath, chapter.GetComicInfoXmlString()); 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) private string[] ParseImageUrlsFromHtml(HtmlDocument document)

View File

@ -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
}

View File

@ -399,16 +399,19 @@ public class Server : GlobalBase
case "Settings/customRequestLimit": case "Settings/customRequestLimit":
if (!requestVariables.TryGetValue("requestType", out string? requestTypeStr) || if (!requestVariables.TryGetValue("requestType", out string? requestTypeStr) ||
!requestVariables.TryGetValue("requestsPerMinute", out string? requestsPerMinuteStr) || !requestVariables.TryGetValue("requestsPerMinute", out string? requestsPerMinuteStr) ||
!requestVariables.TryGetValue("connector", out connectorName) || !Enum.TryParse(requestTypeStr, out RequestType requestType) ||
!byte.TryParse(requestTypeStr, out byte requestType) || !int.TryParse(requestsPerMinuteStr, out int requestsPerMinute))
!int.TryParse(requestsPerMinuteStr, out int requestsPerMinute) ||
!_parent.TryGetConnector(connectorName, out connector))
{ {
SendResponse(HttpStatusCode.BadRequest, response); SendResponse(HttpStatusCode.BadRequest, response);
break; 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; break;
case "NotificationConnectors/Update": case "NotificationConnectors/Update":
if (!requestVariables.TryGetValue("notificationConnector", out string? notificationConnectorStr) || if (!requestVariables.TryGetValue("notificationConnector", out string? notificationConnectorStr) ||

View File

@ -2,6 +2,7 @@
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Newtonsoft.Json; using Newtonsoft.Json;
using Tranga.LibraryConnectors; using Tranga.LibraryConnectors;
using Tranga.MangaConnectors;
using Tranga.NotificationConnectors; using Tranga.NotificationConnectors;
using static System.IO.UnixFileMode; using static System.IO.UnixFileMode;
@ -13,15 +14,26 @@ public class TrangaSettings
public string workingDirectory { get; private set; } public string workingDirectory { get; private set; }
public int apiPortNumber { get; init; } public int apiPortNumber { get; init; }
public string styleSheet { get; private set; } public string styleSheet { get; private set; }
public string userAgent { get; set; } = DefaultUserAgent;
public string userAgent { get; set; } =
$"Tranga ({Enum.GetName(Environment.OSVersion.Platform)}; {(Environment.Is64BitOperatingSystem ? "x64" : "")}) / 1.0";
[JsonIgnore] public string settingsFilePath => Path.Join(workingDirectory, "settings.json"); [JsonIgnore] public string settingsFilePath => Path.Join(workingDirectory, "settings.json");
[JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json"); [JsonIgnore] public string libraryConnectorsFilePath => Path.Join(workingDirectory, "libraryConnectors.json");
[JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json"); [JsonIgnore] public string notificationConnectorsFilePath => Path.Join(workingDirectory, "notificationConnectors.json");
[JsonIgnore] public string jobsFolderPath => Path.Join(workingDirectory, "jobs"); [JsonIgnore] public string jobsFolderPath => Path.Join(workingDirectory, "jobs");
[JsonIgnore] public string coverImageCache => Path.Join(workingDirectory, "imageCache"); [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<RequestType, int> 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<RequestType, int> requestLimits { get; set; } = DefaultRequestLimits;
public TrangaSettings(string? downloadLocation = null, string? workingDirectory = null, int? apiPortNumber = null) public TrangaSettings(string? downloadLocation = null, string? workingDirectory = null, int? apiPortNumber = null)
{ {