MangaConnectors in API

This commit is contained in:
2024-12-15 23:00:35 +01:00
parent 8c5bcd2665
commit 538e6fa60b
32 changed files with 339 additions and 3375 deletions

View File

@ -12,30 +12,30 @@ public class AsuraToon : MangaConnector
this.downloadClient = new ChromiumDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(m => m.Value.Length > 0)).ToLower();
string requestUrl = $"https://asuracomic.net/series?name={sanitizedTitle}";
RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>();
return [];
if (requestResult.htmlDocument is null)
{
return [];
}
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://asuracomic.net/series/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
@ -47,7 +47,7 @@ public class AsuraToon : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
HtmlNodeCollection mangaList = document.DocumentNode.SelectNodes("//a[starts-with(@href,'series')]");
if (mangaList is null || mangaList.Count < 1)
@ -55,24 +55,25 @@ public class AsuraToon : MangaConnector
IEnumerable<string> urls = mangaList.Select(a => $"https://asuracomic.net/{a.GetAttributeValue("href", "")}");
List<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
string? originalLanguage = null;
Dictionary<string, string> altTitles = new(), links = new();
HtmlNodeCollection genreNodes = document.DocumentNode.SelectNodes("//h3[text()='Genres']/../div/button");
string[] tags = genreNodes.Select(b => b.InnerText).ToArray();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
HtmlNode statusNode = document.DocumentNode.SelectSingleNode("//h3[text()='Status']/../h3[2]");
MangaReleaseStatus releaseStatus = statusNode.InnerText.ToLower() switch
@ -102,16 +103,21 @@ public class AsuraToon : MangaConnector
HtmlNodeCollection artistNodes = document.DocumentNode.SelectNodes("//h3[text()='Artist']/../h3[not(text()='Artist' or text()='_')]");
IEnumerable<string> authorNames = authorNodes is null ? [] : authorNodes.Select(a => a.InnerText);
IEnumerable<string> artistNames = artistNodes is null ? [] : artistNodes.Select(a => a.InnerText);
List<string> authors = authorNames.Concat(artistNames).ToList();
List<string> authorStrings = authorNames.Concat(artistNames).ToList();
Author[] authors = authorStrings.Select(author => new Author(author)).ToArray();
HtmlNode? firstChapterNode = document.DocumentNode.SelectSingleNode("//a[contains(@href, 'chapter/1')]/../following-sibling::h3");
uint year = uint.Parse(firstChapterNode?.InnerText.Split(' ')[^1] ?? "2000");
Manga manga = new Manga(publicationId, sortName, description, websiteUrl, coverUrl, null, year,
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name, authors, tags, links, altTitles); //TODO
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
[]);
return manga;
return (manga, authors, mangaTags, [], []);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -13,30 +13,30 @@ public class Bato : MangaConnector
this.downloadClient = new HttpDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
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, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>();
return [];
if (requestResult.htmlDocument is null)
{
return [];
}
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://bato.to/title/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult = downloadClient.MakeRequest(url, RequestType.MangaInfo);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
@ -48,7 +48,7 @@ public class Bato : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
HtmlNode mangaList = document.DocumentNode.SelectSingleNode("//div[@data-hk='0-0-2']");
if (!mangaList.ChildNodes.Any(node => node.Name == "div"))
@ -57,18 +57,18 @@ public class Bato : MangaConnector
List<string> urls = mangaList.ChildNodes
.Select(node => $"https://bato.to{node.Descendants("div").First().FirstChild.GetAttributeValue("href", "")}").ToList();
HashSet<Manga> ret = new();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
HtmlNode infoNode = document.DocumentNode.SelectSingleNode("/html/body/div/main/div[1]/div[2]");
@ -78,24 +78,26 @@ public class Bato : MangaConnector
string[] altTitlesList = infoNode.ChildNodes[1].ChildNodes[2].InnerText.Split('/');
int i = 0;
Dictionary<string, string> altTitles = altTitlesList.ToDictionary(s => i++.ToString(), s => s);
MangaAltTitle[] altTitles = altTitlesList.Select(a => new MangaAltTitle(i++.ToString(), a)).ToArray();
string posterUrl = document.DocumentNode.SelectNodes("//img")
string coverUrl = document.DocumentNode.SelectNodes("//img")
.First(child => child.GetAttributeValue("data-hk", "") == "0-1-0").GetAttributeValue("src", "").Replace("&amp;", "&");
List<HtmlNode> genreNodes = document.DocumentNode.SelectSingleNode("//b[text()='Genres:']/..").SelectNodes("span").ToList();
string[] tags = genreNodes.Select(node => node.FirstChild.InnerText).ToArray();
MangaTag[] mangaTags = tags.Select(s => new MangaTag(s)).ToArray();
List<HtmlNode> authorsNodes = infoNode.ChildNodes[1].ChildNodes[3].Descendants("a").ToList();
List<string> authors = authorsNodes.Select(node => node.InnerText.Replace("amp;", "")).ToList();
List<string> authorNames = authorsNodes.Select(node => node.InnerText.Replace("amp;", "")).ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
HtmlNode? originalLanguageNode = document.DocumentNode.SelectSingleNode("//span[text()='Tr From']/..");
string originalLanguage = originalLanguageNode is not null ? originalLanguageNode.LastChild.InnerText : "";
if (!int.TryParse(
if (!uint.TryParse(
document.DocumentNode.SelectSingleNode("//span[text()='Original Publication:']/..").LastChild.InnerText.Split('-')[0],
out int year))
year = DateTime.Now.Year;
out uint year))
year = (uint)DateTime.Now.Year;
string status = document.DocumentNode.SelectSingleNode("//span[text()='Original Publication:']/..")
.ChildNodes[2].InnerText;
@ -109,8 +111,15 @@ public class Bato : MangaConnector
case "pending": releaseStatus = MangaReleaseStatus.Unreleased; break;
}
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
altTitles.Select(a => a.AltTitleId).ToArray());
return (manga, authors, mangaTags, [], altTitles);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -18,11 +18,11 @@ public abstract class MangaConnector(string name, string[] supportedLanguages, s
public virtual Manga[] Mangas { get; internal set; } = [];
public abstract Manga[] GetManga(string publicationTitle = "");
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "");
public abstract Manga? GetMangaFromUrl(string url);
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url);
public abstract Manga? GetMangaFromId(string publicationId);
public abstract (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId);
public abstract Chapter[] GetChapters(Manga manga, string language="en");

View File

@ -16,12 +16,12 @@ public class MangaDex : MangaConnector
this.downloadClient = new HttpDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
const int limit = 100; //How many values we want returned at once
int offset = 0; //"Page"
int total = int.MaxValue; //How many total results are there, is updated on first request
HashSet<Manga> retManga = new();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> retManga = new();
int loadedPublicationData = 0;
List<JsonNode> results = new();
@ -59,7 +59,7 @@ public class MangaDex : MangaConnector
return retManga.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
RequestResult requestResult =
downloadClient.MakeRequest($"https://api.mangadex.org/manga/{publicationId}?includes%5B%5D=manga&includes%5B%5D=cover_art&includes%5B%5D=author&includes%5B%5D=artist&includes%5B%5D=tag", RequestType.MangaInfo);
@ -71,14 +71,14 @@ public class MangaDex : MangaConnector
return null;
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
Regex idRex = new (@"https:\/\/mangadex.org\/title\/([A-z0-9-]*)\/.*");
string id = idRex.Match(url).Groups[1].Value;
return GetMangaFromId(id);
}
private Manga? MangaFromJsonObject(JsonObject manga)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? MangaFromJsonObject(JsonObject manga)
{
if (!manga.TryGetPropertyValue("id", out JsonNode? idNode))
return null;
@ -90,7 +90,7 @@ public class MangaDex : MangaConnector
if (!attributes.TryGetPropertyValue("title", out JsonNode? titleNode))
return null;
string title = titleNode!.AsObject().ContainsKey("en") switch
string sortName = titleNode!.AsObject().ContainsKey("en") switch
{
true => titleNode.AsObject()["en"]!.GetValue<string>(),
false => titleNode.AsObject().First().Value!.GetValue<string>()
@ -105,6 +105,7 @@ public class MangaDex : MangaConnector
altTitlesDict.TryAdd(altTitleNodeObject.First().Key, altTitleNodeObject.First().Value!.GetValue<string>());
}
}
MangaAltTitle[] altTitles = altTitlesDict.Select(t => new MangaAltTitle(t.Key, t.Value)).ToArray();
if (!attributes.TryGetPropertyValue("description", out JsonNode? descriptionNode))
return null;
@ -118,6 +119,7 @@ public class MangaDex : MangaConnector
if (attributes.TryGetPropertyValue("links", out JsonNode? linksNode) && linksNode is not null)
foreach (KeyValuePair<string, JsonNode?> linkKv in linksNode!.AsObject())
linksDict.TryAdd(linkKv.Key, linkKv.Value.GetValue<string>());
Link[] links = linksDict.Select(x => new Link(x.Key, x.Value)).ToArray();
string? originalLanguage =
attributes.TryGetPropertyValue("originalLanguage", out JsonNode? originalLanguageNode) switch
@ -139,17 +141,17 @@ public class MangaDex : MangaConnector
};
}
int? year = attributes.TryGetPropertyValue("year", out JsonNode? yearNode) switch
uint year = attributes.TryGetPropertyValue("year", out JsonNode? yearNode) switch
{
true => yearNode?.GetValue<int>(),
false => null
true => yearNode?.GetValue<uint>()??0,
false => 0
};
HashSet<string> tags = new(128);
if (attributes.TryGetPropertyValue("tags", out JsonNode? tagsNode))
foreach (JsonNode? tagNode in tagsNode!.AsArray())
tags.Add(tagNode!["attributes"]!["name"]!["en"]!.GetValue<string>());
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
if (!manga.TryGetPropertyValue("relationships", out JsonNode? relationshipsNode))
return null;
@ -161,18 +163,26 @@ public class MangaDex : MangaConnector
string fileName = coverNode["attributes"]!["fileName"]!.GetValue<string>();
string coverUrl = $"https://uploads.mangadex.org/covers/{publicationId}/{fileName}";
List<string> authors = new();
List<string> authorNames = new();
JsonNode?[] authorNodes = relationshipsNode.AsArray()
.Where(rel => rel!["type"]!.GetValue<string>().Equals("author") || rel!["type"]!.GetValue<string>().Equals("artist")).ToArray();
foreach (JsonNode? authorNode in authorNodes)
{
string authorName = authorNode!["attributes"]!["name"]!.GetValue<string>();
if(!authors.Contains(authorName))
authors.Add(authorName);
if(!authorNames.Contains(authorName))
authorNames.Add(authorName);
}
Author[] authors = authorNames.Select(a => new Author(a)).ToArray();
Manga pub = //TODO
return pub;
Manga pub = new (publicationId, sortName, description, $"https://mangadex.org/title/{publicationId}", coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
links.Select(l => l.LinkId).ToArray(),
altTitles.Select(a => a.AltTitleId).ToArray());
return (pub, authors, mangaTags, links, altTitles);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -11,45 +11,45 @@ public class MangaHere : MangaConnector
this.downloadClient = new ChromiumDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = string.Join('+', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://www.mangahere.cc/search?title={sanitizedTitle}";
RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null)
return Array.Empty<Manga>();
return [];
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
if (document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),' container ')]").Any(node => node.ChildNodes.Any(cNode => cNode.HasClass("search-keywords"))))
return Array.Empty<Manga>();
return [];
List<string> urls = document.DocumentNode
.SelectNodes("//a[contains(@href, '/manga/') and not(contains(@href, '.html'))]")
.Select(thumb => $"https://www.mangahere.cc{thumb.GetAttributeValue("href", "")}").Distinct().ToList();
HashSet<Manga> ret = new();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://www.mangahere.cc/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -61,27 +61,29 @@ public class MangaHere : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url);
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new();
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
//We dont get posters, because same origin bs HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//img[contains(concat(' ',normalize-space(@class),' '),' detail-info-cover-img ')]");
string posterUrl = "http://static.mangahere.cc/v20230914/mangahere/images/nopicture.jpg";
string coverUrl = "http://static.mangahere.cc/v20230914/mangahere/images/nopicture.jpg";
HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//span[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-title-font ')]");
string sortName = titleNode.InnerText;
List<string> authors = document.DocumentNode
List<string> authorNames = document.DocumentNode
.SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-say ')]/a")
.Select(node => node.InnerText)
.ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
HashSet<string> tags = document.DocumentNode
.SelectNodes("//p[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-tag-list ')]/a")
.Select(node => node.InnerText)
.ToHashSet();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray();
status = document.DocumentNode.SelectSingleNode("//span[contains(concat(' ',normalize-space(@class),' '),' detail-info-right-title-tip ')]").InnerText;
switch (status.ToLower())
@ -97,8 +99,15 @@ public class MangaHere : MangaConnector
.SelectSingleNode("//p[contains(concat(' ',normalize-space(@class),' '),' fullcontent ')]");
string description = descriptionNode.InnerText;
Manga manga =//TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, 0,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
[]);
return (manga, authors, mangaTags, [], []);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -11,14 +11,14 @@ public class MangaKatana : MangaConnector
this.downloadClient = new HttpDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = string.Join("%20", 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, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>();
return [];
// ReSharper disable once MergeIntoPattern
// If a single result is found, the user will be redirected to the results directly instead of a result page
@ -29,16 +29,16 @@ public class MangaKatana : MangaConnector
return new [] { ParseSinglePublicationFromHtml(requestResult.result, requestResult.redirectedToUrl.Split('/')[^1], requestResult.redirectedToUrl) };
}
Manga[] publications = ParsePublicationsFromHtml(requestResult.result);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.result);
return publications;
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://mangakatana.com/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -47,7 +47,7 @@ public class MangaKatana : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.result, url.Split('/')[^1], url);
}
private Manga[] ParsePublicationsFromHtml(Stream html)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(Stream html)
{
StreamReader reader = new(html);
string htmlString = reader.ReadToEnd();
@ -55,7 +55,7 @@ public class MangaKatana : MangaConnector
document.LoadHtml(htmlString);
IEnumerable<HtmlNode> searchResults = document.DocumentNode.SelectNodes("//*[@id='book_list']/div");
if (searchResults is null || !searchResults.Any())
return Array.Empty<Manga>();
return [];
List<string> urls = new();
foreach (HtmlNode mangaResult in searchResults)
{
@ -63,27 +63,27 @@ public class MangaKatana : MangaConnector
.First(a => a.Name == "href").Value);
}
HashSet<Manga> ret = new();
HashSet<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
private Manga ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(Stream html, string publicationId, string websiteUrl)
{
StreamReader reader = new(html);
string htmlString = reader.ReadToEnd();
HtmlDocument document = new();
document.LoadHtml(htmlString);
Dictionary<string, string> altTitles = new();
Dictionary<string, string> altTitlesDict = new();
Dictionary<string, string>? links = null;
HashSet<string> tags = new();
string[] authors = Array.Empty<string>();
string[] authorNames = [];
string originalLanguage = "";
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
@ -102,10 +102,10 @@ public class MangaKatana : MangaConnector
case "altnames":
string[] alts = value.Split(" ; ");
for (int i = 0; i < alts.Length; i++)
altTitles.Add(i.ToString(), alts[i]);
altTitlesDict.Add(i.ToString(), alts[i]);
break;
case "authorsartists":
authors = value.Split(',');
authorNames = value.Split(',');
break;
case "status":
switch (value.ToLower())
@ -120,29 +120,38 @@ public class MangaKatana : MangaConnector
}
}
string posterUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First()
string coverUrl = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[1]/div").Descendants("img").First()
.GetAttributes().First(a => a.Name == "src").Value;
string description = document.DocumentNode.SelectSingleNode("//*[@id='single_book']/div[3]/p").InnerText;
while (description.StartsWith('\n'))
description = description.Substring(1);
int year = DateTime.Now.Year;
uint year = (uint)DateTime.Now.Year;
string yearString = infoTable.Descendants("div").First(d => d.HasClass("updateAt"))
.InnerText.Split('-')[^1];
if(yearString.Contains("ago") == false)
{
year = Convert.ToInt32(yearString);
year = uint.Parse(yearString);
}
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray();
MangaAltTitle[] altTitles = altTitlesDict.Select(x => new MangaAltTitle(x.Key, x.Value)).ToArray();
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
altTitles.Select(a => a.AltTitleId).ToArray());
return (manga, authors, mangaTags, [], altTitles);
}
public override Chapter[] GetChapters(Manga manga, string language="en")
{
Log($"Getting chapters {manga}");
string requestUrl = $"https://mangakatana.com/manga/{manga.MangaId}";
// Leaving this in for verification if the page exists
RequestResult requestResult =

View File

@ -12,27 +12,27 @@ public class MangaLife : MangaConnector
this.downloadClient = new ChromiumDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = WebUtility.UrlEncode(publicationTitle);
string requestUrl = $"https://manga4life.com/search/?name={sanitizedTitle}";
RequestResult requestResult =
downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>();
return [];
if (requestResult.htmlDocument is null)
return Array.Empty<Manga>();
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://manga4life.com/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
Regex publicationIdRex = new(@"https:\/\/(www\.)?manga4life.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[2].Value;
@ -43,7 +43,7 @@ public class MangaLife : MangaConnector
return null;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
HtmlNode resultsNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']/div[last()]/div[1]/div");
if (resultsNode.Descendants("div").Count() == 1 && resultsNode.Descendants("div").First().HasClass("NoResults"))
@ -51,21 +51,21 @@ public class MangaLife : MangaConnector
return [];
}
HashSet<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (HtmlNode resultNode in resultsNode.SelectNodes("div"))
{
string url = resultNode.Descendants().First(d => d.HasClass("SeriesName")).GetAttributeValue("href", "");
Manga? manga = GetMangaFromUrl($"https://manga4life.com{url}");
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl($"https://manga4life.com{url}");
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new();
@ -73,7 +73,7 @@ public class MangaLife : MangaConnector
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img");
string posterUrl = posterNode.GetAttributeValue("src", "");
string coverUrl = posterNode.GetAttributeValue("src", "");
HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1");
string sortName = titleNode.InnerText;
@ -81,20 +81,22 @@ public class MangaLife : MangaConnector
HtmlNode[] authorsNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Author(s):']/..").Descendants("a")
.ToArray();
List<string> authors = new();
List<string> authorNames = new();
foreach (HtmlNode authorNode in authorsNodes)
authors.Add(authorNode.InnerText);
authorNames.Add(authorNode.InnerText);
Author[] authors = authorNames.Select(a => new Author(a)).ToArray();
HtmlNode[] genreNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a")
.ToArray();
foreach (HtmlNode genreNode in genreNodes)
tags.Add(genreNode.InnerText);
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
HtmlNode yearNode = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a")
.First();
int year = Convert.ToInt32(yearNode.InnerText);
uint year = uint.Parse(yearNode.InnerText);
HtmlNode[] statusNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Status:']/..").Descendants("a")
@ -116,8 +118,15 @@ public class MangaLife : MangaConnector
.Descendants("div").First();
string description = descriptionNode.InnerText;
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
[]);
return (manga, authors, mangaTags, [], []);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -13,7 +13,7 @@ public class Manganato : MangaConnector
this.downloadClient = new HttpDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = string.Join('_', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://manganato.com/search/story/{sanitizedTitle}";
@ -21,11 +21,11 @@ public class Manganato : MangaConnector
downloadClient.MakeRequest(requestUrl, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 ||requestResult.htmlDocument is null)
return [];
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
List<HtmlNode> searchResults = document.DocumentNode.Descendants("div").Where(n => n.HasClass("search-story-item")).ToList();
List<string> urls = new();
@ -35,23 +35,23 @@ public class Manganato : MangaConnector
.First(a => a.Name == "href").Value);
}
HashSet<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add(manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://chapmanganato.com/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -63,12 +63,12 @@ public class Manganato : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, url.Split('/')[^1], url);
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
Dictionary<string, string> altTitles = new();
Dictionary<string, string> altTitlesDict = new();
Dictionary<string, string>? links = null;
HashSet<string> tags = new();
string[] authors = Array.Empty<string>();
string[] authorNames = [];
string originalLanguage = "";
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
@ -89,12 +89,12 @@ public class Manganato : MangaConnector
case "alternative":
string[] alts = value.Split(" ; ");
for(int i = 0; i < alts.Length; i++)
altTitles.Add(i.ToString(), alts[i]);
altTitlesDict.Add(i.ToString(), alts[i]);
break;
case "authors":
authors = value.Split('-');
for (int i = 0; i < authors.Length; i++)
authors[i] = authors[i].Replace("\r\n", "");
authorNames = value.Split('-');
for (int i = 0; i < authorNames.Length; i++)
authorNames[i] = authorNames[i].Replace("\r\n", "");
break;
case "status":
switch (value.ToLower())
@ -111,8 +111,11 @@ public class Manganato : MangaConnector
break;
}
}
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
MangaTag[] mangaTags = tags.Select(n => new MangaTag(n)).ToArray();
MangaAltTitle[] mangaAltTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray();
string posterUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
string coverUrl = document.DocumentNode.Descendants("span").First(s => s.HasClass("info-image")).Descendants("img").First()
.GetAttributes().First(a => a.Name == "src").Value;
string description = document.DocumentNode.Descendants("div").First(d => d.HasClass("panel-story-info-description"))
@ -128,11 +131,18 @@ public class Manganato : MangaConnector
CultureInfo.InvariantCulture).Millisecond);
int year = DateTime.ParseExact(oldestChapter?.GetAttributeValue("title", "Dec 31 2400, 23:59")??"Dec 31 2400, 23:59", pattern,
uint year = (uint)DateTime.ParseExact(oldestChapter?.GetAttributeValue("title", "Dec 31 2400, 23:59")??"Dec 31 2400, 23:59", pattern,
CultureInfo.InvariantCulture).Year;
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
mangaAltTitles.Select(a => a.AltTitleId).ToArray());
return (manga, authors, mangaTags, [], mangaAltTitles);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -23,7 +23,7 @@ public class Mangasee : MangaConnector
public string[] a { get; set; }
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string requestUrl = "https://mangasee123.com/_search.php";
RequestResult requestResult =
@ -41,10 +41,10 @@ public class Mangasee : MangaConnector
string[] urls = filteredResults.Select(result => $"https://mangasee123.com/manga/{result.i}").ToArray();
List<Manga> searchResultManga = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> searchResultManga = new();
foreach (string url in urls)
{
Manga? newManga = GetMangaFromUrl(url);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? newManga = GetMangaFromUrl(url);
if(newManga is { } manga)
searchResultManga.Add(manga);
}
@ -79,12 +79,12 @@ public class Mangasee : MangaConnector
return ret.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://mangasee123.com/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
Regex publicationIdRex = new(@"https:\/\/mangasee123.com\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -95,7 +95,7 @@ public class Mangasee : MangaConnector
return null;
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new();
@ -103,7 +103,7 @@ public class Mangasee : MangaConnector
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
HtmlNode posterNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//img");
string posterUrl = posterNode.GetAttributeValue("src", "");
string coverUrl = posterNode.GetAttributeValue("src", "");
HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//div[@class='BoxBody']//div[@class='row']//h1");
string sortName = titleNode.InnerText;
@ -111,20 +111,22 @@ public class Mangasee : MangaConnector
HtmlNode[] authorsNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Author(s):']/..").Descendants("a")
.ToArray();
List<string> authors = new();
List<string> authorNames = new();
foreach (HtmlNode authorNode in authorsNodes)
authors.Add(authorNode.InnerText);
authorNames.Add(authorNode.InnerText);
Author[] authors = authorNames.Select(a => new Author(a)).ToArray();
HtmlNode[] genreNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Genre(s):']/..").Descendants("a")
.ToArray();
foreach (HtmlNode genreNode in genreNodes)
tags.Add(genreNode.InnerText);
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
HtmlNode yearNode = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Released:']/..").Descendants("a")
.First();
int year = Convert.ToInt32(yearNode.InnerText);
uint year = uint.Parse(yearNode.InnerText);
HtmlNode[] statusNodes = document.DocumentNode
.SelectNodes("//div[@class='BoxBody']//div[@class='row']//span[text()='Status:']/..").Descendants("a")
@ -146,8 +148,15 @@ public class Mangasee : MangaConnector
.Descendants("div").First();
string description = descriptionNode.InnerText;
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
[]);
return (manga, authors, mangaTags, [], []);
}
public override Chapter[] GetChapters(Manga manga, string language="en")
@ -180,7 +189,7 @@ public class Mangasee : MangaConnector
}
catch (HttpRequestException e)
{
return Array.Empty<Chapter>();
return [];
}
}

View File

@ -12,49 +12,49 @@ public class Mangaworld : MangaConnector
this.downloadClient = new HttpDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
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, RequestType.Default);
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300)
return Array.Empty<Manga>();
return [];
if (requestResult.htmlDocument is null)
return Array.Empty<Manga>();
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return [];
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
if (!document.DocumentNode.SelectSingleNode("//div[@class='comics-grid']").ChildNodes
.Any(node => node.HasClass("entry")))
return Array.Empty<Manga>();
return [];
List<string> urls = document.DocumentNode
.SelectNodes(
"//div[@class='comics-grid']//div[@class='entry']//a[contains(concat(' ',normalize-space(@class),' '),'thumb')]")
.Select(thumb => thumb.GetAttributeValue("href", "")).ToList();
HashSet<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://www.mangaworld.ac/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
RequestResult requestResult =
downloadClient.MakeRequest(url, RequestType.MangaInfo);
@ -69,9 +69,9 @@ public class Mangaworld : MangaConnector
return ParseSinglePublicationFromHtml(requestResult.htmlDocument, id, url);
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
Dictionary<string, string> altTitles = new();
Dictionary<string, string> altTitlesDict = new();
Dictionary<string, string>? links = null;
string originalLanguage = "";
MangaReleaseStatus releaseStatus = MangaReleaseStatus.Unreleased;
@ -83,18 +83,20 @@ public class Mangaworld : MangaConnector
HtmlNode metadata = infoNode.Descendants().First(d => d.HasClass("meta-data"));
HtmlNode altTitlesNode = metadata.SelectSingleNode("//span[text()='Titoli alternativi: ' or text()='Titolo alternativo: ']/..").ChildNodes[1];
string[] alts = altTitlesNode.InnerText.Split(", ");
for(int i = 0; i < alts.Length; i++)
altTitles.Add(i.ToString(), alts[i]);
altTitlesDict.Add(i.ToString(), alts[i]);
MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray();
HtmlNode genresNode =
metadata.SelectSingleNode("//span[text()='Generi: ' or text()='Genero: ']/..");
HashSet<string> tags = genresNode.SelectNodes("a").Select(node => node.InnerText).ToHashSet();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
HtmlNode authorsNode =
metadata.SelectSingleNode("//span[text()='Autore: ' or text()='Autori: ']/..");
string[] authors = authorsNode.SelectNodes("a").Select(node => node.InnerText).ToArray();
string[] authorNames = authorsNode.SelectNodes("a").Select(node => node.InnerText).ToArray();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
string status = metadata.SelectSingleNode("//span[text()='Stato: ']/..").SelectNodes("a").First().InnerText;
// ReSharper disable 5 times StringLiteralTypo
@ -107,15 +109,22 @@ public class Mangaworld : MangaConnector
case "in corso": releaseStatus = MangaReleaseStatus.Continuing; break;
}
string posterUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", "");
string coverUrl = document.DocumentNode.SelectSingleNode("//img[@class='rounded']").GetAttributeValue("src", "");
string description = document.DocumentNode.SelectSingleNode("//div[@id='noidungm']").InnerText;
string yearString = metadata.SelectSingleNode("//span[text()='Anno di uscita: ']/..").SelectNodes("a").First().InnerText;
int year = Convert.ToInt32(yearString);
uint year = uint.Parse(yearString);
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
altTitles.Select(a => a.AltTitleId).ToArray());
return (manga, authors, mangaTags, [], altTitles);
}
public override Chapter[] GetChapters(Manga manga, string language="en")
@ -145,16 +154,18 @@ public class Mangaworld : MangaConnector
{
foreach (HtmlNode volNode in document.DocumentNode.SelectNodes("//div[contains(concat(' ',normalize-space(@class),' '),'volume-element')]"))
{
string volume = volumeRex.Match(volNode.SelectNodes("div").First(node => node.HasClass("volume")).SelectSingleNode("p").InnerText).Groups[1].Value;
string volumeStr = volumeRex.Match(volNode.SelectNodes("div").First(node => node.HasClass("volume")).SelectSingleNode("p").InnerText).Groups[1].Value;
float volume = float.Parse(volumeStr);
foreach (HtmlNode chNode in volNode.SelectNodes("div").First(node => node.HasClass("volume-chapters")).SelectNodes("div"))
{
string number = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value;
string numberStr = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value;
float chapter = float.Parse(numberStr);
string url = chNode.SelectSingleNode("a").GetAttributeValue("href", "");
string id = idRex.Match(chNode.SelectSingleNode("a").GetAttributeValue("href", "")).Groups[1].Value;
try
{
ret.Add(new Chapter(manga, null, volume, number, url, id));
ret.Add(new Chapter(manga, url, chapter, volume, null));
}
catch (Exception e)
{
@ -166,12 +177,13 @@ public class Mangaworld : MangaConnector
{
foreach (HtmlNode chNode in chaptersWrapper.SelectNodes("div").Where(node => node.HasClass("chapter")))
{
string number = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value;
string numberStr = chapterRex.Match(chNode.SelectSingleNode("a").SelectSingleNode("span").InnerText).Groups[1].Value;
float chapter = float.Parse(numberStr);
string url = chNode.SelectSingleNode("a").GetAttributeValue("href", "");
string id = idRex.Match(chNode.SelectSingleNode("a").GetAttributeValue("href", "")).Groups[1].Value;
try
{
ret.Add(new Chapter(manga, null, null, number, url, id));
ret.Add(new Chapter(manga, url, chapter, null, null));
}
catch (Exception e)
{

View File

@ -12,7 +12,7 @@ public class ManhuaPlus : MangaConnector
this.downloadClient = new ChromiumDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
string sanitizedTitle = string.Join(' ', Regex.Matches(publicationTitle, "[A-z]*").Where(str => str.Length > 0)).ToLower();
string requestUrl = $"https://manhuaplus.org/search?keyword={sanitizedTitle}";
@ -21,37 +21,37 @@ public class ManhuaPlus : MangaConnector
if ((int)requestResult.statusCode < 200 || (int)requestResult.statusCode >= 300 || requestResult.htmlDocument is null)
return [];
Manga[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] publications = ParsePublicationsFromHtml(requestResult.htmlDocument);
return publications;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
if (document.DocumentNode.SelectSingleNode("//h1/../..").ChildNodes//I already want to not.
.Any(node => node.InnerText.Contains("No manga found")))
return Array.Empty<Manga>();
return [];
List<string> urls = document.DocumentNode
.SelectNodes("//h1/../..//a[contains(@href, 'https://manhuaplus.org/manga/') and contains(concat(' ',normalize-space(@class),' '),' clamp ') and not(contains(@href, '/chapter'))]")
.Select(mangaNode => mangaNode.GetAttributeValue("href", "")).ToList();
HashSet<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (string url in urls)
{
Manga? manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? manga = GetMangaFromUrl(url);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://manhuaplus.org/manga/{publicationId}");
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
Regex publicationIdRex = new(@"https:\/\/manhuaplus.org\/manga\/(.*)(\/.*)*");
string publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -62,7 +62,7 @@ public class ManhuaPlus : MangaConnector
return null;
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
string originalLanguage = "", status = "";
Dictionary<string, string> altTitles = new(), links = new();
@ -71,23 +71,24 @@ public class ManhuaPlus : MangaConnector
HtmlNode posterNode = document.DocumentNode.SelectSingleNode("/html/body/main/div/div/div[2]/div[1]/figure/a/img");//BRUH
Regex posterRex = new(@".*(\/uploads/covers/[a-zA-Z0-9\-\._\~\!\$\&\'\(\)\*\+\,\;\=\:\@]+).*");
string posterUrl = $"https://manhuaplus.org/{posterRex.Match(posterNode.GetAttributeValue("src", "")).Groups[1].Value}";
string coverUrl = $"https://manhuaplus.org/{posterRex.Match(posterNode.GetAttributeValue("src", "")).Groups[1].Value}";
HtmlNode titleNode = document.DocumentNode.SelectSingleNode("//h1");
string sortName = titleNode.InnerText.Replace("\n", "");
List<string> authors = new();
List<string> authorNames = new();
try
{
HtmlNode[] authorsNodes = document.DocumentNode
.SelectNodes("//a[contains(@href, 'https://manhuaplus.org/authors/')]")
.ToArray();
foreach (HtmlNode authorNode in authorsNodes)
authors.Add(authorNode.InnerText);
authorNames.Add(authorNode.InnerText);
}
catch (ArgumentNullException e)
{
}
Author[] authors = authorNames.Select(a => new Author(a)).ToArray();
try
{
@ -99,11 +100,12 @@ public class ManhuaPlus : MangaConnector
catch (ArgumentNullException e)
{
}
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
Regex yearRex = new(@"(?:[0-9]{1,2}\/){2}([0-9]{2,4}) [0-9]{1,2}:[0-9]{1,2}");
HtmlNode yearNode = document.DocumentNode.SelectSingleNode("//aside//i[contains(concat(' ',normalize-space(@class),' '),' fa-clock ')]/../span");
Match match = yearRex.Match(yearNode.InnerText);
int year = match.Success && match.Groups[1].Success ? int.Parse(match.Groups[1].Value) : 1960;
uint year = match.Success && match.Groups[1].Success ? uint.Parse(match.Groups[1].Value) : 0;
status = document.DocumentNode.SelectSingleNode("//aside//i[contains(concat(' ',normalize-space(@class),' '),' fa-rss ')]/../span").InnerText.Replace("\n", "");
switch (status.ToLower())
@ -119,8 +121,15 @@ public class ManhuaPlus : MangaConnector
.SelectSingleNode("//div[@id='syn-target']");
string description = descriptionNode.InnerText;
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
[]);
return (manga, authors, mangaTags, [], []);
}
public override Chapter[] GetChapters(Manga manga, string language="en")

View File

@ -17,7 +17,7 @@ public class Weebcentral : MangaConnector
downloadClient = new ChromiumDownloadClient();
}
public override Manga[] GetManga(string publicationTitle = "")
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] GetManga(string publicationTitle = "")
{
const int limit = 32; //How many values we want returned at once
var offset = 0; //"Page"
@ -36,7 +36,7 @@ public class Weebcentral : MangaConnector
return publications;
}
private Manga[] ParsePublicationsFromHtml(HtmlDocument document)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])[] ParsePublicationsFromHtml(HtmlDocument document)
{
if (document.DocumentNode.SelectNodes("//article") == null)
return [];
@ -44,18 +44,18 @@ public class Weebcentral : MangaConnector
var urls = document.DocumentNode.SelectNodes("/html/body/article/a[@class='link link-hover']")
.Select(elem => elem.GetAttributeValue("href", "")).ToList();
HashSet<Manga> ret = new();
List<(Manga, Author[], MangaTag[], Link[], MangaAltTitle[])> ret = new();
foreach (var url in urls)
{
var manga = GetMangaFromUrl(url);
if (manga is not null)
ret.Add((Manga)manga);
if (manga is { } x)
ret.Add(x);
}
return ret.ToArray();
}
public override Manga? GetMangaFromUrl(string url)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromUrl(string url)
{
Regex publicationIdRex = new(@"https:\/\/weebcentral\.com\/series\/(\w*)\/(.*)");
var publicationId = publicationIdRex.Match(url).Groups[1].Value;
@ -67,22 +67,24 @@ public class Weebcentral : MangaConnector
return null;
}
private Manga ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
private (Manga, Author[], MangaTag[], Link[], MangaAltTitle[]) ParseSinglePublicationFromHtml(HtmlDocument document, string publicationId, string websiteUrl)
{
var posterNode =
document.DocumentNode.SelectSingleNode("//section[@class='flex items-center justify-center']/picture/img");
var posterUrl = posterNode?.GetAttributeValue("src", "") ?? "";
var coverUrl = posterNode?.GetAttributeValue("src", "") ?? "";
var titleNode = document.DocumentNode.SelectSingleNode("//section/h1");
var sortName = titleNode?.InnerText ?? "Undefined";
HtmlNode[] authorsNodes =
document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Author(s): ']/span")?.ToArray() ?? [];
var authors = authorsNodes.Select(n => n.InnerText).ToList();
var authorNames = authorsNodes.Select(n => n.InnerText).ToList();
Author[] authors = authorNames.Select(n => new Author(n)).ToArray();
HtmlNode[] genreNodes =
document.DocumentNode.SelectNodes("//ul/li[strong/text() = 'Tags(s): ']/span")?.ToArray() ?? [];
HashSet<string> tags = genreNodes.Select(n => n.InnerText).ToHashSet();
MangaTag[] mangaTags = tags.Select(t => new MangaTag(t)).ToArray();
var statusNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Status: ']/a");
var status = statusNode?.InnerText ?? "";
@ -96,24 +98,32 @@ public class Weebcentral : MangaConnector
}
var yearNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Released: ']/span");
var year = Convert.ToInt32(yearNode?.InnerText ?? "0");
var year = uint.Parse(yearNode?.InnerText ?? "0");
var descriptionNode = document.DocumentNode.SelectSingleNode("//ul/li[strong/text() = 'Description']/p");
var description = descriptionNode?.InnerText ?? "Undefined";
HtmlNode[] altTitleNodes = document.DocumentNode
.SelectNodes("//ul/li[strong/text() = 'Associated Name(s)']/ul/li")?.ToArray() ?? [];
Dictionary<string, string> altTitles = new(), links = new();
Dictionary<string, string> altTitlesDict = new(), links = new();
for (var i = 0; i < altTitleNodes.Length; i++)
altTitles.Add(i.ToString(), altTitleNodes[i].InnerText);
altTitlesDict.Add(i.ToString(), altTitleNodes[i].InnerText);
MangaAltTitle[] altTitles = altTitlesDict.Select(a => new MangaAltTitle(a.Key, a.Value)).ToArray();
var originalLanguage = "";
Manga manga = //TODO
return manga;
Manga manga = new (publicationId, sortName, description, websiteUrl, coverUrl, null, year,
originalLanguage, releaseStatus, -1, null, null,
this.Name,
authors.Select(a => a.AuthorId).ToArray(),
mangaTags.Select(t => t.Tag).ToArray(),
[],
altTitles.Select(a => a.AltTitleId).ToArray());
return (manga, authors, mangaTags, [], altTitles);
}
public override Manga? GetMangaFromId(string publicationId)
public override (Manga, Author[], MangaTag[], Link[], MangaAltTitle[])? GetMangaFromId(string publicationId)
{
return GetMangaFromUrl($"https://weebcentral.com/series/{publicationId}");
}