public static void Merge(this MangaObject mangaObject, IEnumerable <MangaObject> list) { if (list.Count() > 0) { // Name if (String.IsNullOrWhiteSpace(mangaObject.Name)) { MangaObject FoD_Obj = list.FirstOrDefault(mO => !String.IsNullOrWhiteSpace(mO.Name)); if (FoD_Obj != null) { mangaObject.Name = FoD_Obj.Name; } } // Released if (mangaObject.Released.Equals(DateTime.MinValue)) { MangaObject FoD_Obj = list.FirstOrDefault(mO => mO.Released > DateTime.MinValue); if (FoD_Obj != null) { mangaObject.Released = FoD_Obj.Released; } } // Rating if (mangaObject.Rating < 0) { MangaObject FoD_Obj = list.FirstOrDefault(mO => mO.Rating >= 0); if (FoD_Obj != null) { mangaObject.Rating = FoD_Obj.Rating; } } // Description if (String.IsNullOrWhiteSpace(mangaObject.Description)) { MangaObject FoD_Obj = list.FirstOrDefault(mO => !String.IsNullOrWhiteSpace(mO.Description)); if (FoD_Obj != null) { mangaObject.Description = FoD_Obj.Description; } } // MangaType & PageFlowDirection if (mangaObject.MangaType == Enums.MangaObjectType.Unknown) { MangaObject FoD_Obj = list.FirstOrDefault(mO => mO.MangaType != Enums.MangaObjectType.Unknown); if (FoD_Obj != null) { mangaObject.MangaType = FoD_Obj.MangaType; mangaObject.PageFlowDirection = FoD_Obj.PageFlowDirection; } } // Locations foreach (List <LocationObject> Locations in (from MangaObject obj in list where obj != null select obj.Locations)) { foreach (LocationObject Location in Locations) { if (!mangaObject.Locations.Any(o => o.ExtensionName == Location.ExtensionName)) { mangaObject.Locations.Add(Location); } else { // Update existing location url Int32 idx = mangaObject.Locations.FindIndex(o => o.ExtensionName == Location.ExtensionName); mangaObject.Locations.RemoveAt(idx); mangaObject.Locations.Insert(idx, Location); } } } // DatabaseLocations foreach (List <LocationObject> DatabaseLocations in (from MangaObject obj in list where obj != null select obj.DatabaseLocations)) { foreach (LocationObject DatabaseLocation in DatabaseLocations) { if (!mangaObject.DatabaseLocations.Any(o => o.ExtensionName == DatabaseLocation.ExtensionName)) { mangaObject.DatabaseLocations.Add(DatabaseLocation); } } } // Artists foreach (List <String> Artists in (from MangaObject obj in list where obj != null select obj.Artists)) { foreach (String Artist in Artists) { if (Artist != null && !mangaObject.Artists.Any(o => o.ToLower() == Artist.ToLower())) { mangaObject.Artists.Add(Artist); } } } // Authors foreach (List <String> Authors in (from MangaObject obj in list where obj != null select obj.Authors)) { foreach (String Author in Authors) { if (Author != null && !mangaObject.Authors.Any(o => o.ToLower() == Author.ToLower())) { mangaObject.Authors.Add(Author); } } } // Covers foreach (List <LocationObject> Covers in (from MangaObject obj in list where obj != null select obj.CoverLocations)) { foreach (LocationObject Cover in Covers) { if (Cover != null && !mangaObject.CoverLocations.Any(o => Equals(o.Url, Cover.Url))) { mangaObject.CoverLocations.Add(Cover); } } } mangaObject.CoverLocations.RemoveAll(c => String.IsNullOrWhiteSpace(c.Url)); // AlternateNames foreach (String Name in (from MangaObject obj in list where obj != null select obj.Name)) { if (!mangaObject.AlternateNames.Any(o => o.ToLower() == Name.ToLower()) && Name != null) { mangaObject.AlternateNames.Add(Name); } } foreach (List <String> AlternateNames in (from MangaObject obj in list where obj != null select obj.AlternateNames)) { foreach (String AlternateName in AlternateNames) { if (AlternateName != null && !mangaObject.AlternateNames.Any(o => o.ToLower() == AlternateName.ToLower())) { mangaObject.AlternateNames.Add(AlternateName); } } } // Genres foreach (List <String> Genres in (from MangaObject obj in list where obj != null select obj.Genres)) { foreach (String Genre in Genres) { if (Genre != null && !mangaObject.Genres.Any(o => o.ToLower() == Genre.ToLower())) { mangaObject.Genres.Add(Genre); } } } // Chapters foreach (List <ChapterObject> Chapters in (from MangaObject obj in list where obj != null select obj.Chapters)) { foreach (ChapterObject Chapter in Chapters) { if (Chapter != null) { ChapterObject chapterObject = mangaObject.Chapters.FirstOrDefault(o => { if (!Int32.Equals(o.Chapter, Chapter.Chapter)) { return(false); } if (!Int32.Equals(o.SubChapter, Chapter.SubChapter)) { return(false); } return(true); }); if (ChapterObject.Equals(chapterObject, null)) { mangaObject.Chapters.Add(Chapter); } else { chapterObject.Merge(Chapter); } } } } mangaObject.SortChapters(); } }
private static void OnCacheObjectChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) { MangaCacheObject control = source as MangaCacheObject; if (!Equals(control.MangaObject, null)) { // MangaObject exists // Use the first ChapterObject or null for the ResumeChapterObject ChapterObject ResumeChapterObject = control.MangaObject.Chapters.FirstOrDefault(); if (!Equals(control.BookmarkObject, null)) { // BookmarkObject exists control.IsNewManga = false; // A BookmarkObject exists, lookup the ResumeChapterObject ResumeChapterObject = control.MangaObject.ChapterObjectOfBookmarkObject(control.BookmarkObject); Double ChapterProgressMaxValue = control.MangaObject.Chapters.Count, ChapterProgressValue = control.MangaObject.IndexOfChapterObject(ResumeChapterObject), ChapterProgressStep = 1 / ChapterProgressMaxValue, ChapterProgress = ChapterProgressValue / ChapterProgressMaxValue, ChapterSubProgress = 1; if (control.BookmarkObject.LastPage > 0) { ChapterSubProgress = (Double)control.BookmarkObject.Page / (Double)control.BookmarkObject.LastPage; } // Calculate and round the readers progress for chapters control.ChapterProgress = (Int32)Math.Round((ChapterProgress + (ChapterSubProgress * ChapterProgressStep)) * 100); ChapterObject LastChapterObject = control.MangaObject.Chapters.LastOrDefault(); if (!Equals(LastChapterObject, null)) { // If there is a ChapterObject to resume, check to see if there are more chapters/pages to read. control.HasMoreToRead = false; if (!control.HasMoreToRead) { control.HasMoreToRead = control.BookmarkObject.Volume < LastChapterObject.Volume; } if (!control.HasMoreToRead) { control.HasMoreToRead = control.BookmarkObject.Chapter < LastChapterObject.Chapter; } if (!control.HasMoreToRead) { control.HasMoreToRead = control.BookmarkObject.SubChapter < LastChapterObject.SubChapter; } if (!control.HasMoreToRead) { control.HasMoreToRead = control.BookmarkObject.Page < control.BookmarkObject.LastPage; } } } else { control.IsNewManga = true; control.ChapterProgress = 0; control.HasMoreToRead = !Equals(ResumeChapterObject, null); } // Store boolean for if the ResumeChapterObject is null Boolean ResumeChapterIsNull = Equals(ResumeChapterObject, null); String MangaObjectChaptersDirectory = System.IO.Path.Combine(control.App.CORE.CHAPTER_ARCHIVE_DIRECTORY, control.MangaObject.MangaFileName()); control.ChapterCacheObjects = new List <ChapterCacheObject>( from ChapterObject in control.MangaObject.Chapters select new ChapterCacheObject(control.MangaObject, ChapterObject) { // If ResumeChapterIsNull is true, there is no ResumeChapter IsResumeChapter = ResumeChapterIsNull ? false : Equals(ChapterObject, ResumeChapterObject), // Store if the ChapterObject archive exists locally IsLocal = ChapterObject.IsLocal( MangaObjectChaptersDirectory, control.App.CORE.CHAPTER_ARCHIVE_EXTENSION) }); } else { control.IsNewManga = false; control.HasMoreToRead = false; control.ChapterProgress = 0; } }
public void Init(ChapterObject chapterObject, Menu.OnChapterAction loadAction) { chapterName.text = chapterObject.chapterName; image.sprite = chapterObject.image; button.onClick.AddListener(() => loadAction.Invoke(chapterObject)); }
public static Boolean IsLocal(this ChapterObject value, String Folder, String Extension = "zip") { return((value != null) ? System.IO.File.Exists(System.IO.Path.Combine(Folder, value.ChapterArchiveName(Extension))) : false); }
public static PageObject PageObjectOfBookmarkObject(this ChapterObject value, BookmarkObject bookmark_object) { return(value.Pages.Find(p => p.PageNumber == bookmark_object.Page)); }
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 }); }
public async Task DownloadAsync(MangaObject MangaObject, ChapterObject ChapterObject, IProgress <Int32> ProgressReporter = null) { String CK = CacheKey(MangaObject, ChapterObject); if (ActiveDownloadsCache.Contains(CK)) { return; } ActiveDownloadsCache.Set(CK, true, DateTimeOffset.MaxValue); try { await Task.Delay(TimeSpan.FromSeconds(1)); // Load the ChapterObject via Async and LimitedTaskFactory IProgress <Int32> ChapterObjectProgressReporter = new Progress <Int32>(ProgressValue => { if (!Equals(ProgressReporter, null)) { ProgressReporter.Report((Int32)Math.Round((Double)ProgressValue * 0.25)); } }); ChapterObject = await ContentTaskFactory.StartNew(() => LoadChapterObjectAsync( MangaObject, ChapterObject, cts.Token, ChapterObjectProgressReporter)).Unwrap(); if (!ChapterObject.Pages.Count.Equals(0)) { // Check for pages // Save the ChapterObject via Async to Save Path with Retry and Timeout of 30min await StoreChapterObject(MangaObject, ChapterObject); // Load the PageObjects from the ChapterObject List <Task <PageObject> > PageObjectDownloadTasks = new List <Task <PageObject> >(); foreach (PageObject PageObject in ChapterObject.Pages) { PageObjectDownloadTasks.Add(ContentTaskFactory.StartNew(() => LoadPageObjectAsync( MangaObject, ChapterObject, PageObject, cts.Token, null)).Unwrap()); } foreach (Task <PageObject> PageObjectDownloadTask in PageObjectDownloadTasks) { PageObject PageObject = await PageObjectDownloadTask; Int32 index = ChapterObject.Pages.FindIndex(_PageObject => Equals(_PageObject.PageNumber, PageObject.PageNumber)); ChapterObject.Pages[index] = PageObject; } await StoreChapterObject(MangaObject, ChapterObject); // Download Images IEnumerable <Task> DownloadImageTasksQuery = from PageObject in ChapterObject.Pages select DownloadImageAsync( PageObject.ImgUrl, PageObject.Url, CORE.SiteExtensions.First(_SiteExtension => PageObject.Url.Contains(_SiteExtension.ExtensionDescriptionAttribute.URLFormat)).Cookies, SavePath(MangaObject, ChapterObject), Path.GetFileName(new Uri(PageObject.ImgUrl).LocalPath)); List <Task> DownloadImageTasks = DownloadImageTasksQuery.ToList(); Int32 OriginalDownloadImageTasksCount = DownloadImageTasks.Count; while (DownloadImageTasks.Count > 0) { Task completedTask = await Task.WhenAny(DownloadImageTasks); DownloadImageTasks.Remove(completedTask); Int32 DownloadImageTasksProgress = (Int32)Math.Round(((Double)(OriginalDownloadImageTasksCount - DownloadImageTasks.Count) / (Double)OriginalDownloadImageTasksCount) * 75); if (!Equals(ProgressReporter, null)) { ProgressReporter.Report(25 + DownloadImageTasksProgress); } } } } catch (Exception ex) { if (!Equals(CORE.Logger, null)) { CORE.Logger.Warn(String.Format("[ContentDownloadManager] An exception was thrown while processing {0}.", MangaObject.Name), ex); } throw ex; } finally { if (!Equals(ProgressReporter, null)) { ProgressReporter.Report(100); } ActiveDownloadsCache.Remove(CK); } }
public static void Merge(this ChapterObject chapterObject, params ChapterObject[] list) { chapterObject.Merge(list.AsEnumerable()); }
public static Int32 IndexOfPageObject(this ChapterObject value, PageObject page_object) { return(value.Pages.FindIndex(p => p.PageNumber == page_object.PageNumber)); }
public ChapterObjectEntry(ChapterObject chapterObject) { _chapterObject = chapterObject; }
public MangaObject ParseMangaObject(string content) { HtmlDocument MangaObjectDocument = new HtmlDocument(); MangaObjectDocument.LoadHtml(content); HtmlNode InformationNode = MangaObjectDocument.DocumentNode.SelectSingleNode("//div[contains(@class,'ipsBox')]/div"); String Cover = InformationNode.SelectSingleNode(".//div[1]/img").Attributes["src"].Value; HtmlNode MangaProperties = InformationNode.SelectSingleNode(".//table[contains(@class,'ipb_table')]"), ChapterListing = MangaObjectDocument.DocumentNode.SelectSingleNode("//table[contains(@class,'chapters_list')]"); String MangaName = HtmlEntity.DeEntitize(MangaObjectDocument.DocumentNode.SelectSingleNode("//h1[contains(@class,'ipsType_pagetitle')]").InnerText.Trim()), MangaTypeProp = HtmlEntity.DeEntitize(MangaProperties.SelectSingleNode(".//tr[5]/td[2]").InnerText), Desciption = HtmlEntity.DeEntitize(MangaProperties.SelectSingleNode(".//tr[7]/td[2]").InnerText.Replace("<br>", "\n")); MangaObjectType MangaType = MangaObjectType.Unknown; FlowDirection PageFlowDirection = FlowDirection.RightToLeft; switch (MangaTypeProp.ToLower()) { default: MangaType = MangaObjectType.Unknown; PageFlowDirection = FlowDirection.RightToLeft; break; case "manga (japanese)": MangaType = MangaObjectType.Manga; PageFlowDirection = FlowDirection.RightToLeft; break; case "manhwa (korean)": MangaType = MangaObjectType.Manhwa; PageFlowDirection = FlowDirection.LeftToRight; break; case "manhua (chinese)": MangaType = MangaObjectType.Manhua; PageFlowDirection = FlowDirection.LeftToRight; break; } HtmlNodeCollection AlternateNameNodes = MangaProperties.SelectSingleNode(".//tr[1]/td[2]").SelectNodes(".//span"), GenreNodes = MangaProperties.SelectSingleNode(".//tr[4]/td[2]").SelectNodes(".//a/span"); String[] AlternateNames = { }, Authors = { HtmlEntity.DeEntitize(MangaProperties.SelectSingleNode(".//tr[2]/td[2]/a").InnerText) }, Artists = { HtmlEntity.DeEntitize(MangaProperties.SelectSingleNode(".//tr[3]/td[2]/a").InnerText) }, Genres = { }; if (AlternateNameNodes != null && AlternateNameNodes.Count > 0) { AlternateNames = (from HtmlNode AltNameNode in AlternateNameNodes select HtmlEntity.DeEntitize(AltNameNode.InnerText.Trim())).ToArray(); } if (GenreNodes != null && GenreNodes.Count > 0) { Genres = (from HtmlNode GenreNode in GenreNodes select HtmlEntity.DeEntitize(GenreNode.InnerText.Trim())).ToArray(); } List <ChapterObject> Chapters = new List <ChapterObject>(); HtmlNodeCollection ChapterNodes = ChapterListing.SelectNodes(String.Format(".//tr[contains(@class,'lang_{0} chapter_row')]", ExtensionDescriptionAttribute.Language)); if (ChapterNodes != null && ChapterNodes.Count > 0) { foreach (HtmlNode ChapterNode in ChapterNodes) { HtmlNode VolChapNameNode = ChapterNode.SelectSingleNode("td[1]/a"); Match VolChapMatch = Regex.Match(VolChapNameNode.InnerText, @"(Vol\.(?<Volume>\d+)\s)?(Ch\.(?<Chapter>\d+))(\.(?<SubChapter>\d+))?"); String ChapterName = VolChapNameNode.InnerText.Substring(VolChapMatch.Length + 2).Trim(), ReleaseData = ReleaseData = ChapterNode.SelectSingleNode("td[5]").InnerText; ChapterObject PrevChapter = Chapters.LastOrDefault(); UInt32 Volume = 0, Chapter = 0, SubChapter = 0; if (VolChapMatch.Groups["Volume"].Success) { UInt32.TryParse(VolChapMatch.Groups["Volume"].Value, out Volume); } if (VolChapMatch.Groups["Chapter"].Success) { UInt32.TryParse(VolChapMatch.Groups["Chapter"].Value, out Chapter); } if (VolChapMatch.Groups["SubChapter"].Success) { UInt32.TryParse(VolChapMatch.Groups["SubChapter"].Value, out SubChapter); } DateTime Released = DateTime.Now; if (ReleaseData.Contains("-")) { ReleaseData = ReleaseData.Split(new String[] { " - " }, StringSplitOptions.RemoveEmptyEntries)[0]; DateTime.TryParseExact(ReleaseData, "dd MMMM yyyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out Released); } else if (ReleaseData.EndsWith("ago")) { String[] ReleaseDataParts = ReleaseData.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); Double Offset = 1; if (!Double.TryParse(ReleaseDataParts[0], out Offset)) { Offset = 1; } Offset *= -1; switch (ReleaseDataParts[1].ToLower()) { default: case "seconds": Released = Released.AddSeconds(Offset); break; case "minutes": Released = Released.AddMinutes(Offset); break; case "hours": Released = Released.AddHours(Offset); break; case "days": Released = Released.AddDays(Offset); break; case "weeks": Released = Released.AddDays(7 * Offset); break; } } String ChapterUrl = VolChapNameNode.Attributes["href"].Value; String ChapterHash = ChapterUrl.Split('#').Last().Split('_').First(); ChapterUrl = String.Format("https://bato.to/areader?id={0}&p=1&supress_webtoon=t", ChapterHash); ChapterObject chapterObject = new ChapterObject() { Name = HtmlEntity.DeEntitize(ChapterName), Volume = Volume, Chapter = Chapter, SubChapter = SubChapter, Released = Released, Locations = { new LocationObject() { ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language, Url = ChapterUrl } } }; if (!Chapters.Any(o => o.Chapter == chapterObject.Chapter && ((Int32)o.SubChapter - chapterObject.SubChapter).InRange(-4, 4))) { Chapters.Add(chapterObject); } else { Chapters.Find(o => o.Chapter == chapterObject.Chapter && ((Int32)o.SubChapter - chapterObject.SubChapter).InRange(-4, 4)).Merge(chapterObject); } } } Chapters.Reverse(); Double Rating = -1; try { HtmlNode RatingNode = MangaObjectDocument.DocumentNode.SelectSingleNode("//div[contains(@class,'rating')]"); String RatingText = new String(RatingNode.InnerText.Trim().Substring(1, 4).Where(IsValidRatingChar).ToArray()); Double.TryParse(RatingText, out Rating); } catch { } return(new MangaObject() { Name = MangaName, MangaType = MangaType, PageFlowDirection = PageFlowDirection, Description = HtmlEntity.DeEntitize(Desciption), AlternateNames = AlternateNames.ToList(), CoverLocations = { new LocationObject() { Url = Cover, ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language } }, Authors = Authors.ToList(), Artists = Artists.ToList(), Genres = Genres.ToList(), Released = (Chapters.FirstOrDefault() ?? new ChapterObject()).Released, Chapters = Chapters, Rating = Rating }); }
public MangaObject ParseMangaObject(String content) { Int32 MangaInformationContentStart = content.IndexOf("<!-- Intro Series -->"), MangaInformationContentEnd = content.IndexOf("<!-- **END: Intro Series -->", MangaInformationContentStart); String MangaInformationContent = content.Substring(MangaInformationContentStart, MangaInformationContentEnd - MangaInformationContentStart); Int32 MangaChaptersContentStart = content.IndexOf("<!-- Main Content -->"), MangaChaptersContentEnd = content.IndexOf("<!-- **END: Main Content -->", MangaChaptersContentStart); String MangaChaptersContent = content.Substring(MangaChaptersContentStart, MangaChaptersContentEnd - MangaChaptersContentStart); HtmlDocument MangaObjectDocument = new HtmlDocument(); MangaObjectDocument.LoadHtml(MangaInformationContent); HtmlNode MangaObjectNode = MangaObjectDocument.DocumentNode.SelectSingleNode(".//div/div"); String MangaName = String.Empty, Description = String.Empty; List <String> AlternateNames = new List <String>(), AuthorsArtists = new List <String>(), Genres = new List <String>(); foreach (HtmlNode DetailNode in MangaObjectNode.SelectNodes(".//div[2]/div[contains(@class,'row')]")) { HtmlNode DetailTypeNode = DetailNode.SelectSingleNode(".//div[1]/b[1] | .//div[1]/strong[1]"), DetailTextNode = (DetailTypeNode != null) ? DetailTypeNode.NextSibling : null, DetailDescriptionNode = (DetailTextNode != null) ? DetailTextNode.NextSibling : null, MangaNameNode = DetailNode.SelectSingleNode(".//div[1]/h1"); HtmlNodeCollection DetailLinkNodes = DetailNode.SelectNodes(".//div[1]/a"); String DetailType = (DetailTypeNode != null) ? DetailTypeNode.InnerText.Trim().TrimEnd(':') : "MangaName", DetailValue = String.Empty; String[] DetailValues = { }; if (DetailLinkNodes != null) { DetailValues = (from HtmlNode LinkNode in DetailLinkNodes select HtmlEntity.DeEntitize(LinkNode.InnerText.Trim())).ToArray(); } else if (MangaNameNode != null) { DetailValue = HtmlEntity.DeEntitize(MangaNameNode.InnerText.Trim()); } else if (DetailDescriptionNode != null) { DetailValue = HtmlEntity.DeEntitize(DetailDescriptionNode.InnerText.Trim()); } else if (DetailTextNode != null) { DetailValue = HtmlEntity.DeEntitize(DetailTextNode.InnerText.Trim()); } switch (DetailType) { default: break; case "MangaName": MangaName = DetailValue; break; case "Alternate Names": AlternateNames = (from String AltName in DetailValue.Split(',') select HtmlEntity.DeEntitize(AltName.Trim())).ToList(); break; case "Author": AuthorsArtists = DetailValues.ToList(); break; case "Genre": Genres = DetailValues.ToList(); break; case "Description": Description = DetailValue; break; } } String Cover = ExtensionDescriptionAttribute.RootUrl + MangaObjectNode.SelectSingleNode(".//div[1]/img/@src").Attributes["src"].Value; List <ChapterObject> Chapters = new List <ChapterObject>(); MangaObjectDocument.LoadHtml(MangaChaptersContent); HtmlNodeCollection RawChapterList = MangaObjectDocument.DocumentNode.SelectNodes(".//div[contains(@class,'row')]"); foreach (HtmlNode RawChapterNode in RawChapterList.Skip(1)) { HtmlNode ChapterNumberNode = RawChapterNode.SelectSingleNode(".//div[1]/a"), ReleaseDate = RawChapterNode.SelectSingleNode(".//div[2]/time"); String ChapterNumber = Regex.Match(ChapterNumberNode.InnerText, @"\d+(\.\d+)?").Value; String[] ChapterSub = ChapterNumber.Trim().Split('.'); DateTime Released = DateTime.Now; String ReleasedTxt = ReleaseDate.InnerText.ToLower(); if (ReleasedTxt.StartsWith("today")) { Released = DateTime.Today; } else if (ReleasedTxt.StartsWith("yesterday")) { Released = DateTime.Today.AddDays(-1); } else if (ReleasedTxt.EndsWith("ago")) { Int32 tDelta = 0; Int32.TryParse(ReleasedTxt.Split(' ')[0], out tDelta); if (ReleasedTxt.Contains("hours")) { Released = Released.AddHours(0 - tDelta); } if (ReleasedTxt.Contains("minutes")) { Released = Released.AddMinutes(0 - tDelta); } if (ReleasedTxt.Contains("seconds")) { Released = Released.AddSeconds(0 - tDelta); } } else { Released = DateTime.Parse(ReleasedTxt); } ChapterObject Chapter = new ChapterObject() { Name = HtmlEntity.DeEntitize(RawChapterNode.SelectSingleNode(".//div[1]/gray").InnerText), Chapter = UInt32.Parse(ChapterSub[0]), Locations = { new LocationObject() { ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language, Url = ExtensionDescriptionAttribute.RootUrl + ChapterNumberNode.Attributes["href"].Value }, }, Released = Released }; if (ChapterSub.Length == 2) { Chapter.SubChapter = UInt32.Parse(ChapterSub[1]); } Chapters.Add(Chapter); } Chapters.Reverse(); MangaObject MangaObj = new MangaObject() { Name = HtmlEntity.DeEntitize(MangaName), Description = HtmlEntity.DeEntitize(Description), AlternateNames = AlternateNames.ToList(), CoverLocations = { new LocationObject() { Url = Cover, ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language } }, Authors = AuthorsArtists.ToList(), Artists = AuthorsArtists.ToList(), Genres = Genres.ToList(), Released = Chapters.First().Released, Chapters = Chapters }; MangaObj.AlternateNames.RemoveAll(an => an.ToLower().Equals("none")); MangaObj.Genres.RemoveAll(g => g.ToLower().Equals("none")); return(MangaObj); }
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(); } }
public void Download(MangaObject MangaObject, ChapterObject ChapterObject, PageObject PageObject, IProgress <Int32> ProgressReporter = null) { Task.Run(() => DownloadAsync(MangaObject, ChapterObject, PageObject, ProgressReporter)); }
public static Int32 IndexOfChapterObject(this MangaObject value, ChapterObject chapter_object) { return(value.Chapters.FindIndex(c => c.Volume == chapter_object.Volume && c.Chapter == chapter_object.Chapter && c.SubChapter == chapter_object.SubChapter)); }
public static void Merge(this ChapterObject chapterObject, IEnumerable <ChapterObject> list) { if (list.Count() > 0) { // Name Regex nameRgx = new Regex(@"^vol(ume)?[\d\s]+.+?ch(apter)?[\d\s]+"); if (String.IsNullOrWhiteSpace(chapterObject.Name) || nameRgx.IsMatch(chapterObject.Name.ToLower())) { ChapterObject FoD_Obj = list.FirstOrDefault(cO => !String.IsNullOrWhiteSpace(cO.Name) && !nameRgx.IsMatch(cO.Name.ToLower())); if (FoD_Obj != null) { chapterObject.Name = FoD_Obj.Name; } } // Volume foreach (UInt32 Volume in (from ChapterObject obj in list where obj != null select obj.Volume)) { if (chapterObject.Volume <= 0 && Volume >= 0) { chapterObject.Volume = Volume; } } // Chapter foreach (UInt32 Chapter in (from ChapterObject obj in list where obj != null select obj.Chapter)) { if (chapterObject.Chapter <= 0 && Chapter >= 0) { chapterObject.Chapter = Chapter; } } // SubChapter foreach (UInt32 SubChapter in (from ChapterObject obj in list where obj != null select obj.SubChapter)) { if (chapterObject.SubChapter < 0 && SubChapter >= 0) { chapterObject.SubChapter = SubChapter; } } // Locations foreach (List <LocationObject> Locations in (from ChapterObject obj in list where obj != null select obj.Locations)) { foreach (LocationObject Location in Locations) { if (!chapterObject.Locations.Any(o => o.ExtensionName == Location.ExtensionName)) { chapterObject.Locations.Add(Location); } else { // Update existing location url Int32 idx = chapterObject.Locations.FindIndex(o => o.ExtensionName == Location.ExtensionName); chapterObject.Locations.RemoveAt(idx); chapterObject.Locations.Insert(idx, Location); } } } // Pages if (Int32.Equals(chapterObject.Pages.Count, 0)) { chapterObject.Pages.AddRange(list.First().Pages); } } }
public MangaObject ParseMangaObject(string content) { if (content.ToLower().Contains("has been licensed, it is not available in MangaHere.".ToLower())) { return(null); } HtmlDocument MangaObjectDocument = new HtmlDocument(); MangaObjectDocument.LoadHtml(content); HtmlNode TitleNode = MangaObjectDocument.DocumentNode.SelectSingleNode("//h1[contains(@class,'title')]"), MangaDetailsNode = MangaObjectDocument.DocumentNode.SelectSingleNode("//div[contains(@class,'manga_detail')]"), MangaPropertiesNode = MangaDetailsNode.SelectSingleNode(".//div[1]"), MangaDesciptionNode = MangaObjectDocument.GetElementbyId("show"), AuthorsNode = MangaPropertiesNode.SelectSingleNode(".//ul/li[5]"), ArtistsNode = MangaPropertiesNode.SelectSingleNode(".//ul/li[6]"), GenresNode = MangaPropertiesNode.SelectSingleNode(".//ul/li[4]"); HtmlNodeCollection AuthorsNodeCollection = AuthorsNode.SelectNodes(".//a"), ArtistsNodeCollection = ArtistsNode.SelectNodes(".//a"); String Desciption = MangaDesciptionNode != null ? MangaDesciptionNode.FirstChild.InnerText : String.Empty, MangaName = HtmlEntity.DeEntitize(System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(TitleNode.LastChild.InnerText.ToLower())); String[] AlternateNames = MangaPropertiesNode.SelectSingleNode(".//ul/li[3]").LastChild.InnerText.Split(new String[] { "; " }, StringSplitOptions.RemoveEmptyEntries), Authors = { }, Artists = { }, Genres = MangaPropertiesNode.SelectSingleNode(".//ul/li[4]").LastChild.InnerText.Split(new String[] { ", " }, StringSplitOptions.RemoveEmptyEntries); if (AuthorsNodeCollection != null) { Authors = (from HtmlNode AuthorNode in AuthorsNodeCollection select HtmlEntity.DeEntitize(AuthorNode.InnerText)).ToArray(); } if (ArtistsNodeCollection != null) { Artists = (from HtmlNode ArtistNode in ArtistsNodeCollection select HtmlEntity.DeEntitize(ArtistNode.InnerText)).ToArray(); } List <ChapterObject> Chapters = new List <ChapterObject>(); HtmlNodeCollection RawChapterList = MangaDetailsNode.SelectNodes(".//div[contains(@class,'detail_list')]/ul[1]/li"); foreach (HtmlNode ChapterNode in RawChapterList) { String volNode = ChapterNode.SelectSingleNode(".//span[1]/span").InnerText; String[] volChapSub = { (volNode != null && volNode.Length > 0) ? volNode.Substring(4).Trim() : "0" }; String ChapterTitle = ChapterNode.SelectSingleNode(".//span[1]/a").InnerText.Trim(); String ChapterNumber = ChapterTitle.Substring(ChapterTitle.LastIndexOf(' ') + 1).Trim(); volChapSub = volChapSub.Concat(ChapterNumber.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries)).ToArray(); ChapterObject Chapter = new ChapterObject() { Name = ChapterTitle, Volume = UInt32.Parse(volChapSub[0]), Chapter = UInt32.Parse(volChapSub[1]), Locations = { new LocationObject() { ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language, Url = ChapterNode.SelectSingleNode(".//span[1]/a").Attributes["href"].Value } }, Released = ChapterNode.SelectSingleNode(".//span[2]").InnerText.ToLower().Equals("today") ? DateTime.Today : (ChapterNode.SelectSingleNode(".//span[2]").InnerText.ToLower().Equals("yesterday") ? DateTime.Today.AddDays(-1) : DateTime.Parse(ChapterNode.SelectSingleNode(".//span[2]").InnerText)) }; if (volChapSub.Length == 3) { Chapter.SubChapter = UInt32.Parse(volChapSub[2]); } Chapters.Add(Chapter); } Chapters.Reverse(); String Cover = MangaPropertiesNode.SelectSingleNode(".//img[1]/@src").Attributes["src"].Value; Cover = Cover.Substring(0, Cover.LastIndexOf('?')); MangaObject MangaObj = new MangaObject() { Name = HtmlEntity.DeEntitize(MangaName), Description = HtmlEntity.DeEntitize(Desciption), AlternateNames = (from AltName in AlternateNames select HtmlEntity.DeEntitize(AltName)).ToList(), CoverLocations = { new LocationObject() { Url = Cover, ExtensionName = ExtensionDescriptionAttribute.Name, ExtensionLanguage = ExtensionDescriptionAttribute.Language } }, Authors = Authors.ToList(), Artists = Artists.ToList(), Genres = Genres.ToList(), Released = Chapters.First().Released, Chapters = Chapters }; MangaObj.AlternateNames.RemoveAll(an => an.ToLower().Equals("none")); MangaObj.Genres.RemoveAll(g => g.ToLower().Equals("none")); return(MangaObj); }
//Yes the archive is a zip file, read the docs public static String ChapterArchiveName(this ChapterObject value, String Extension = "zip") { return(String.Format("{0}.{1}.{2}.{3}", value.Volume, value.Chapter, value.SubChapter, Extension)); }
public ReadChapterRequestObject(MangaObject MangaObject, ChapterObject ChapterObject) { this.MangaObject = MangaObject; this.ChapterObject = ChapterObject; }
public String CacheKey(MangaObject MangaObject, ChapterObject ChapterObject, PageObject PageObject) { return(String.Format("{0}/{1}/{2}", MangaObject.MangaArchiveName(CORE.MANGA_ARCHIVE_EXTENSION), ChapterObject.ChapterArchiveName(CORE.CHAPTER_ARCHIVE_EXTENSION), PageObject.PageNumber)); }