using HtmlAgilityPack; using MangaReader.Core.Metadata; namespace MangaReader.Core.Sources.NatoManga.Metadata; public class NatoMangaWebCrawler : MangaWebCrawler { public override string SourceId => "NatoManga"; public override async Task GetMangaAsync(string url, CancellationToken cancellationToken) { HtmlDocument document = await GetHtmlDocumentAsync(url, cancellationToken); NatoMangaHtmlDocument node = new(document); SourceManga manga = new() { Title = node.TitleNode?.InnerText ?? string.Empty, Genres = GetGenres(node.GenresNode), Chapters = GetChapters(node.ChapterNodes) }; return manga; } private static List GetGenres(HtmlNode? node) { if (node == null) return []; HtmlNodeCollection? genreNodes = node.SelectNodes(".//a"); if (genreNodes == null) return []; return [.. genreNodes.Select(genreNode => genreNode.InnerText.Trim())]; } private static long GetViews(HtmlNode node) { string text = node.InnerText.Trim(); if (int.TryParse(text, out int number)) return number; if (double.TryParse(text, out double doubleNumber)) return (int)doubleNumber; ReadOnlySpan shortText = text.AsSpan(0, text.Length - 1); if (double.TryParse(shortText, out double formattedNumber) == false) return 0; char suffix = text[^1]; //if (char.GetNumericValue(suffix) > -1) // return (int)formattedNumber; long multiplier = GetMultiplier(suffix); return (int)(formattedNumber * multiplier); } private static long GetMultiplier(char c) { return c switch { 'K' => 1_000, 'M' => 1_000_000, 'B' => 1_000_000_000, 'T' => 1_000_000_000_000, _ => 0, }; } private static List GetChapters(HtmlNodeCollection? chapterNodes) { List chapters = []; if (chapterNodes == null) return chapters; foreach (var node in chapterNodes) { HtmlNodeCollection? chapterPropertyNodes = node.SelectNodes(".//span"); if (chapterPropertyNodes == null || chapterPropertyNodes.Count < 3) continue; HtmlNode? chapterNameNode = chapterPropertyNodes[0].SelectSingleNode(".//a"); HtmlNode chapterViewNode = chapterPropertyNodes[1]; HtmlNode chapterTimeNode = chapterPropertyNodes[2]; if (chapterNameNode == null) continue; SourceMangaChapter chapter = new() { Number = GetChapterNumber(chapterNameNode), Name = chapterNameNode.InnerText, Url = chapterNameNode.Attributes["href"].Value, Views = GetViews(chapterViewNode), UploadDate = DateTime.Parse(chapterTimeNode.Attributes["title"].Value) }; chapters.Add(chapter); } return chapters; } private static float GetChapterNumber(HtmlNode chapterNameNode) { string url = chapterNameNode.Attributes["href"].Value; int index = url.IndexOf("/chapter-"); if (index == -1) return 0; string chapterNumber = url[(index + "/chapter-".Length)..].Replace('-', '.'); return float.Parse(chapterNumber); } }