public MangaObject ParseMangaObject(string Content) { HtmlDocument MangaObjectDocument = new HtmlDocument(); MangaObjectDocument.LoadHtml(Content); HtmlNode MangaNode = MangaObjectDocument.DocumentNode.SelectSingleNode(".//section[contains(@class, 'manga')]"); String Name = HtmlEntity.DeEntitize(MangaNode.SelectSingleNode(".//div/div[1]/h1/a").InnerText); Name = Name.Substring(0, Name.LastIndexOf(' ')); HtmlNode AlternateNamesNode = MangaNode.SelectSingleNode(".//div/table/tr/td[2]/table/tr[4]/td"); List <String> AlternateNames = (from AltName in HtmlEntity.DeEntitize(AlternateNamesNode.InnerText).Split(';') select AltName.Trim()).ToList(); List <String> Authors = (from Node in MangaNode.SelectNodes(".//div/table/tr/td[2]/table/tr[5]/td/a") select HtmlEntity.DeEntitize(Node.InnerText)).ToList(); List <String> Artists = (from Node in MangaNode.SelectNodes(".//div/table/tr/td[2]/table/tr[6]/td/a") select HtmlEntity.DeEntitize(Node.InnerText)).ToList(); List <String> Genres = (from Node in MangaNode.SelectNodes(".//div/table/tr/td[2]/table/tr[7]/td/a") select HtmlEntity.DeEntitize(Node.InnerText)).ToList(); // Detect type MangaObjectType MangaType = MangaObjectType.Unknown; String mType = MangaNode.SelectSingleNode(".//div/table/tr/td[2]/table/tr[8]/td").InnerText.ToLower(); if (mType.Contains("japanese manga")) { MangaType = MangaObjectType.Manga; } else if (mType.Contains("korean manhwa")) { MangaType = MangaObjectType.Manhwa; } // Get description String Description = HtmlEntity.DeEntitize(MangaNode.SelectSingleNode(".//div/p").InnerText); // Chapters List <ChapterObject> Chapters = new List <ChapterObject>(); foreach (HtmlNode ChapterVersionNode in MangaNode.SelectNodes(".//*[@id='list']/div[starts-with(@id, 'stream_')]")) { foreach (HtmlNode VolumeNode in ChapterVersionNode.SelectNodes(".//div[contains(@class, 'volume')]")) { UInt32 Volume = 0; HtmlNode VolumeNameNode = VolumeNode.SelectSingleNode(".//h4"); if (!Equals(VolumeNameNode, null)) { String[] idParts = VolumeNameNode.GetAttributeValue("id", "v-1-").Split('-'); UInt32.TryParse(idParts[2], out Volume); } foreach (HtmlNode ChapterNode in ChapterVersionNode.SelectNodes(".//div/ul/li")) { HtmlNode InfoNode = ChapterNode.SelectSingleNode(".//a"); String ChapterName = HtmlEntity.DeEntitize(InfoNode.InnerText), Url = InfoNode.GetAttributeValue("href", null); UInt32 Chapter = 0, SubChapter = 0; Match match = Regex.Match(ChapterName, @"(vol\.(?<Volume>\d+)\s)?ch\.(?<Chapter>\d+)(\.(?<SubChapter>\d+))?"); if (match.Success) { if (match.Groups["Volume"].Success) { UInt32.TryParse(match.Groups["Volume"].Value, out Volume); } if (match.Groups["Chapter"].Success) { UInt32.TryParse(match.Groups["Chapter"].Value, out Chapter); } if (match.Groups["SubChapter"].Success) { UInt32.TryParse(match.Groups["SubChapter"].Value, out SubChapter); } } if (Equals(Url, null)) { continue; } Url = String.Format("{0}{1}", ExtensionDescriptionAttribute.RootUrl, Url); ChapterObject NewChapterObject = new ChapterObject() { Name = ChapterName, Volume = Volume, Chapter = Chapter, SubChapter = SubChapter, Locations = { new LocationObject() { Enabled = true, ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language, Url = Url } } }; ChapterObject ExistingChapterObject = Chapters.FirstOrDefault(o => { if (!Int32.Equals(o.Chapter, NewChapterObject.Chapter)) { return(false); } if (!Int32.Equals(o.SubChapter, NewChapterObject.SubChapter)) { return(false); } return(true); }); if (Equals(ExistingChapterObject, null)) { Chapters.Add(NewChapterObject); } else { ExistingChapterObject.Merge(NewChapterObject); } } } } Chapters = Chapters.OrderBy(c => c.Chapter).ThenBy(c => c.SubChapter).ThenBy(c => c.Volume).ToList(); return(new MangaObject() { Name = Name, AlternateNames = AlternateNames, Description = Description, Authors = Authors, Artists = Artists, Genres = Genres, MangaType = MangaType, Chapters = Chapters }); }
private async Task <ChapterObject> LoadChapterObjectAsync(MangaObject MangaObject, ChapterObject ChapterObject, CancellationToken ct, IProgress <Int32> progress) { try { await TaskConcurrencySemaphore.WaitAsync(ct); ct.ThrowIfCancellationRequested(); // Store valid ISiteExtension IEnumerable <ISiteExtension> ValidSiteExtensions = ValidExtensions(CORE.SiteExtensions, CORE.UserConfiguration.EnabledExtensions).Cast <ISiteExtension>(); // Re-Order the Chapter's LocationObjects to the EnabledExtensions order. IEnumerable <LocationObject> OrderedChapterObjectLocations = from EnExt in CORE.UserConfiguration.EnabledExtensions where ChapterObject.Locations.Exists(LocObj => EnExt.EqualsLocationObject(LocObj)) select ChapterObject.Locations.FirstOrDefault(LocObj => EnExt.EqualsLocationObject(LocObj)); foreach (LocationObject LocationObject in OrderedChapterObjectLocations) { ct.ThrowIfCancellationRequested(); ISiteExtension SiteExtension = ValidSiteExtensions.FirstOrDefault(_ => LocationObjectExtension(_, LocationObject)); if (Equals(SiteExtension, null)) { continue; // Continue with the foreach loop } ct.ThrowIfCancellationRequested(); using (WebDownloader WebDownloader = new WebDownloader(SiteExtension.Cookies)) { WebDownloader.Encoding = System.Text.Encoding.UTF8; WebDownloader.Referer = SiteExtension.ExtensionDescriptionAttribute.RefererHeader; DownloadProgressChangedEventHandler ProgressEventHandler = (s, e) => { if (!Equals(progress, null)) { progress.Report((Int32)Math.Round((Double)e.ProgressPercentage * 0.9)); } ct.ThrowIfCancellationRequested(); }; WebDownloader.DownloadProgressChanged += ProgressEventHandler; String ChapterWebContent = await WebDownloader.DownloadStringTaskAsync(LocationObject.Url).Retry(DOWNLOAD_TIMEOUT); ChapterObject DownloadedChapterObject = SiteExtension.ParseChapterObject(ChapterWebContent); WebDownloader.DownloadProgressChanged -= ProgressEventHandler; ct.ThrowIfCancellationRequested(); if (!Equals(DownloadedChapterObject, null)) { ChapterObject.Merge(DownloadedChapterObject); ChapterObject.Pages = DownloadedChapterObject.Pages; break; // Break free of the foreach loop } } } if (!Equals(progress, null)) { progress.Report(100); } ct.ThrowIfCancellationRequested(); return(ChapterObject); } finally { TaskConcurrencySemaphore.Release(); } }