public async Task<AnimeStaffData> GetCharStaffData(bool force = false) { if (!_animeMode) throw new InvalidOperationException("Umm you said it's going to be manga..."); var output = force ? new AnimeStaffData() : await DataCache.RetrieveData<AnimeStaffData>($"staff_{_animeId}", "AnimeDetails", 7) ?? new AnimeStaffData(); if (output.AnimeCharacterPairs.Count > 0 || output.AnimeStaff.Count > 0) return output; var raw = await GetRequestResponse(false); if (string.IsNullOrEmpty(raw)) return null; var doc = new HtmlDocument(); doc.LoadHtml(raw); try { var mainContainer = doc.FirstOfDescendantsWithClass("div", "js-scrollfix-bottom-rel"); var tables = mainContainer.ChildNodes.Where(node => node.Name == "table").ToList(); int i = 0; foreach (var table in tables.Take(tables.Count - 1)) { try { var current = new AnimeCharacterStaffModel(); var imgs = table.Descendants("img").ToList(); var infos = table.Descendants("td").ToList(); //2nd is character 4th is person //character var img = imgs[0].Attributes["data-src"].Value; if (!img.Contains("questionmark")) { img = img.Replace("/r/46x64", ""); current.AnimeCharacter.ImgUrl = img.Substring(0, img.IndexOf('?')); } current.AnimeCharacter.FromAnime = _animeMode; current.AnimeCharacter.ShowId = _animeId.ToString(); current.AnimeCharacter.Name = WebUtility.HtmlDecode(imgs[0].Attributes["alt"].Value.Replace(",", "")); current.AnimeCharacter.Id = infos[1].ChildNodes[1].Attributes["href"].Value.Split('/')[2]; current.AnimeCharacter.Notes = infos[1].ChildNodes[3].InnerText; //voiceactor try { img = imgs[1].Attributes["data-src"].Value; if (!img.Contains("questionmark")) { img = img.Replace("/r/46x64", ""); current.AnimeStaffPerson.ImgUrl = img.Substring(0, img.IndexOf('?')); } current.AnimeStaffPerson.Name = WebUtility.HtmlDecode(imgs[1].Attributes["alt"].Value.Replace(",", "")); current.AnimeStaffPerson.Id = infos[3].ChildNodes[1].Attributes["href"].Value.Split('/')[2]; current.AnimeStaffPerson.Notes = infos[3].ChildNodes[4].InnerText; } catch (Exception) { //no voice actor current.AnimeStaffPerson.Name = "Unknown"; current.AnimeStaffPerson.IsUnknown = true; } output.AnimeCharacterPairs.Add(current); if (i++ > 30) break; } catch (Exception) { //oddities } } i = 0; foreach (var staffRow in tables.Last().Descendants("tr")) { try { var current = new AnimeStaffPerson(); var imgs = staffRow.Descendants("img").ToList(); var info = staffRow.Descendants("td").Last(); //we want last var img = imgs[0].Attributes["data-src"].Value; if (!img.Contains("questionmark")) { img = img.Replace("/r/46x64", ""); current.ImgUrl = img.Substring(0, img.IndexOf('?')); } current.Name = WebUtility.HtmlDecode(imgs[0].Attributes["alt"].Value.Replace(",", "")); current.Id = info.ChildNodes[0].Attributes["href"].Value.Split('/')[2]; current.Notes = info.ChildNodes[3].InnerText; output.AnimeStaff.Add(current); if (i++ > 30) break; } catch (Exception) { //what can I say? } } } catch (Exception) { //mysteries of html } DataCache.SaveData(output,$"staff_{_animeId}","AnimeDetails"); return output; }
private async void NavigatePersonWebPage(AnimeStaffPerson person) { ResourceLocator.SystemControlsLauncherService.LaunchUri(new Uri($"https://myanimelist.net/people/{person.Id}")); }
public async Task<ProfileData> GetProfileData(bool force = false, bool updateFavsOnly = false) { ProfileData possibleData = null; if (!force) possibleData = await DataCache.RetrieveProfileData(_userName); if (possibleData != null) return possibleData; var raw = !updateFavsOnly ? await (await (await MalHttpContextProvider.GetHttpContextAsync()).GetAsync($"/profile/{_userName}")).Content.ReadAsStringAsync() : await GetRequestResponse(); var doc = new HtmlDocument(); doc.LoadHtml(raw); var current = new ProfileData { User = { Name = _userName } }; #region Recents try { var i = 1; foreach ( var recentNode in doc.DocumentNode.Descendants("div") .Where( node => node.Attributes.Contains("class") && node.Attributes["class"].Value == HtmlClassMgr.ClassDefs["#Profile:recentUpdateNode:class"])) { if (i <= 3) { current.RecentAnime.Add( int.Parse( recentNode.Descendants("a").First().Attributes["href"].Value.Substring(8).Split('/')[2])); } else { current.RecentManga.Add( int.Parse( recentNode.Descendants("a").First().Attributes["href"].Value.Substring(8).Split('/')[2])); } i++; } } catch (Exception) { //no recents } #endregion #region FavChar try { foreach ( var favCharNode in doc.DocumentNode.Descendants("ul") .First( node => node.Attributes.Contains("class") && node.Attributes["class"].Value == HtmlClassMgr.ClassDefs["#Profile:favCharacterNode:class"]) .Descendants("li")) { var curr = new AnimeCharacter(); var imgNode = favCharNode.Descendants("a").First(); var styleString = imgNode.Attributes["style"].Value.Substring(22); curr.ImgUrl = styleString.Replace("/r/80x120", ""); curr.ImgUrl = curr.ImgUrl.Substring(0, curr.ImgUrl.IndexOf('?')); var infoNode = favCharNode.Descendants("div").Skip(1).First(); var nameNode = infoNode.Descendants("a").First(); curr.Name = nameNode.InnerText.Trim(); curr.Id = nameNode.Attributes["href"].Value.Substring(9).Split('/')[2]; var originNode = infoNode.Descendants("a").Skip(1).First(); curr.Notes = originNode.InnerText.Trim(); curr.ShowId = originNode.Attributes["href"].Value.Split('/')[2]; curr.FromAnime = originNode.Attributes["href"].Value.Split('/')[1] == "anime"; current.FavouriteCharacters.Add(curr); } } catch (Exception) { //no favs } #endregion #region FavManga try { foreach ( var favMangaNode in doc.DocumentNode.Descendants("ul") .First( node => node.Attributes.Contains("class") && node.Attributes["class"].Value == HtmlClassMgr.ClassDefs["#Profile:favMangaNode:class"]) .Descendants("li")) { current.FavouriteManga.Add( int.Parse( favMangaNode.Descendants("a").First().Attributes["href"].Value.Substring(9).Split('/')[2 ])); } } catch (Exception) { //no favs } #endregion #region FavAnime try { foreach ( var favAnimeNode in doc.DocumentNode.Descendants("ul") .First( node => node.Attributes.Contains("class") && node.Attributes["class"].Value == HtmlClassMgr.ClassDefs["#Profile:favAnimeNode:class"]) .Descendants("li")) { current.FavouriteAnime.Add( int.Parse( favAnimeNode.Descendants("a").First().Attributes["href"].Value.Substring(9).Split('/')[2 ])); } } catch (Exception) { //no favs } #endregion #region FavPpl try { foreach ( var favPersonNode in doc.DocumentNode.Descendants("ul") .First( node => node.Attributes.Contains("class") && node.Attributes["class"].Value == HtmlClassMgr.ClassDefs["#Profile:favPeopleNode:class"]) .Descendants("li")) { var curr = new AnimeStaffPerson(); var aElems = favPersonNode.Descendants("a"); var styleString = aElems.First().Attributes["style"].Value.Substring(22); curr.ImgUrl = styleString.Replace("/r/80x120", ""); curr.ImgUrl = curr.ImgUrl.Substring(0, curr.ImgUrl.IndexOf('?')); curr.Name = aElems.Skip(1).First().InnerText.Trim(); curr.Id = aElems.Skip(1).First().Attributes["href"].Value.Substring(9).Split('/')[2]; current.FavouritePeople.Add(curr); } } catch (Exception) { //no favs } #endregion #region Stats if (!updateFavsOnly) try { var animeStats = doc.FirstOfDescendantsWithClass("div", "stats anime"); var generalStats = animeStats.Descendants("div").First().Descendants("div"); current.AnimeDays = float.Parse(generalStats.First().InnerText.Substring(5).Trim()); current.AnimeMean = float.Parse(generalStats.Last().InnerText.Substring(11).Trim()); var i = 0; #region AnimeStats foreach ( var htmlNode in animeStats.FirstOfDescendantsWithClass("ul", "stats-status fl-l").Descendants("li")) { switch (i) { case 0: current.AnimeWatching = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 1: current.AnimeCompleted = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 2: current.AnimeOnHold = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 3: current.AnimeDropped = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 4: current.AnimePlanned = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; } i++; } //left stats done now right i = 0; foreach ( var htmlNode in animeStats.FirstOfDescendantsWithClass("ul", "stats-data fl-r").Descendants("li")) { switch (i) { case 0: current.AnimeTotal = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; case 1: current.AnimeRewatched = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; case 2: current.AnimeEpisodes = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; } i++; } //we are done with anime #endregion i = 0; animeStats = doc.FirstOfDescendantsWithClass("div", "stats manga"); generalStats = animeStats.Descendants("div").First().Descendants("div"); current.MangaDays = float.Parse(generalStats.First().InnerText.Substring(5).Trim()); current.MangaMean = float.Parse(generalStats.Last().InnerText.Substring(11).Trim()); #region MangaStats foreach ( var htmlNode in animeStats.FirstOfDescendantsWithClass("ul", "stats-status fl-l").Descendants("li")) { switch (i) { case 0: current.MangaReading = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 1: current.MangaCompleted = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 2: current.MangaOnHold = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 3: current.MangaDropped = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; case 4: current.MangaPlanned = int.Parse(htmlNode.Descendants("span").First().InnerText.Trim().Replace(",", "")); break; } i++; } //left stats done now right i = 0; foreach ( var htmlNode in animeStats.FirstOfDescendantsWithClass("ul", "stats-data fl-r").Descendants("li")) { switch (i) { case 0: current.MangaTotal = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; case 1: current.MangaReread = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; case 2: current.MangaChapters = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; case 3: current.MangaVolumes = int.Parse(htmlNode.Descendants("span").Last().InnerText.Trim().Replace(",", "")); break; } i++; } //we are done with manga #endregion } catch (Exception) { //hatml } #endregion #region LeftSideBar if (!updateFavsOnly) try { var sideInfo = doc.FirstOfDescendantsWithClass("ul", "user-status border-top pb8 mb4").Descendants("li").ToList(); try { foreach (var htmlNode in sideInfo) { current.Details.Add(new Tuple<string, string>(htmlNode.FirstChild.InnerText, htmlNode.LastChild.InnerText)); } //current.LastOnline = sideInfo[0].LastChild.InnerText; //current.Gender = sideInfo[1].LastChild.InnerText; //current.Birthday = sideInfo[2].LastChild.InnerText; //current.Location = sideInfo[3].LastChild.InnerText; //current.Joined = sideInfo[4].LastChild.InnerText; } catch (Exception) { //current.LastOnline = sideInfo[0].LastChild.InnerText; //current.Joined = sideInfo[1].LastChild.InnerText; } current.User.ImgUrl = doc.FirstOfDescendantsWithClass("div", "user-image mb8").Descendants("img").First().Attributes["src" ] .Value; } catch (Exception) { //??? } #endregion #region Friends if (!updateFavsOnly) try { var friends = doc.FirstOfDescendantsWithClass("div", "user-friends pt4 pb12").Descendants("a"); foreach (var friend in friends) { var curr = new MalUser(); var styleString = friend.Attributes["style"].Value.Substring(22); curr.ImgUrl = styleString.Replace("/r/76x120", ""); curr.ImgUrl = curr.ImgUrl.Substring(0, curr.ImgUrl.IndexOf('?')); curr.Name = friend.InnerText; current.Friends.Add(curr); } } catch (Exception) { // } #endregion #region Comments if (!updateFavsOnly) try { var commentBox = doc.FirstOfDescendantsWithClass("div", "user-comments mt24 pt24"); foreach (var comment in commentBox.WhereOfDescendantsWithClass("div", "comment clearfix")) { var curr = new MalComment(); curr.User.ImgUrl = comment.Descendants("img").First().Attributes["src"].Value; var textBlock = comment.Descendants("div").First(); var header = textBlock.Descendants("div").First(); curr.User.Name = header.ChildNodes[1].InnerText; curr.Date = header.ChildNodes[3].InnerText; curr.Content = WebUtility.HtmlDecode(textBlock.Descendants("div").Skip(1).First().InnerText.Trim()); var postActionNodes = comment.WhereOfDescendantsWithClass("a", "ml8"); var convNode = postActionNodes.FirstOrDefault(node => node.InnerText.Trim() == "Conversation"); if (convNode != null) { curr.ComToCom = WebUtility.HtmlDecode(convNode.Attributes["href"].Value.Split('?').Last()); } var deleteNode = postActionNodes.FirstOrDefault(node => node.InnerText.Trim() == "Delete"); if(deleteNode != null) { curr.CanDelete = true; curr.Id = deleteNode.Attributes["onclick"].Value.Split(new char[] {'(', ')'}, StringSplitOptions.RemoveEmptyEntries).Last(); } current.Comments.Add(curr); } } catch (Exception e) { //no comments } #endregion try { current.ProfileMemId = doc.DocumentNode.Descendants("input") .First(node => node.Attributes.Contains("name") && node.Attributes["name"].Value == "profileMemId") .Attributes["value"].Value; } catch (Exception) { //restricted } try { current.HtmlContent = doc.FirstOfDescendantsWithClass("div", "word-break").OuterHtml; } catch (Exception) { // } if (_userName == Credentials.UserName) //umm why do we need someone's favs? { FavouritesManager.ForceNewSet(FavouriteType.Anime, current.FavouriteAnime.Select(i => i.ToString()).ToList()); FavouritesManager.ForceNewSet(FavouriteType.Manga, current.FavouriteManga.Select(i => i.ToString()).ToList()); FavouritesManager.ForceNewSet(FavouriteType.Character, current.FavouriteCharacters.Select(i => i.Id).ToList()); FavouritesManager.ForceNewSet(FavouriteType.Person, current.FavouritePeople.Select(i => i.Id).ToList()); } if (!updateFavsOnly) DataCache.SaveProfileData(_userName, current); return current; }
public async Task<CharacterDetailsData> GetCharacterDetails(bool force = false) { var possibleData = force ? null : await DataCache.RetrieveData<CharacterDetailsData>(_id.ToString(), "character_details", 30); if (possibleData != null) return possibleData; var output = new CharacterDetailsData(); var raw = await GetRequestResponse(); if (string.IsNullOrEmpty(raw)) return output; var doc = new HtmlDocument(); doc.LoadHtml(raw); output.Id = _id; try { var columns = doc.DocumentNode.Descendants("table").First().ChildNodes[1].ChildNodes.Where(node => node.Name == "td").ToList(); var leftColumn = columns[0]; var tables = leftColumn.Descendants("table"); foreach (var table in tables) { foreach (var descendant in table.Descendants("tr")) { var links = descendant.Descendants("a").ToList(); if (links[0].Attributes["href"].Value.StartsWith("/anime")) { var curr = new AnimeLightEntry { IsAnime = true }; curr.Id = int.Parse(links[0].Attributes["href"].Value.Split('/')[2]); var img = links[0].Descendants("img").First().Attributes["src"].Value; if (!img.Contains("questionmark")) { img = Regex.Replace(img, @"\/r\/\d+x\d+", ""); curr.ImgUrl = img.Substring(0, img.IndexOf('?')); } curr.Title = WebUtility.HtmlDecode(links[1].InnerText.Trim()); output.Animeography.Add(curr); } else { var curr = new AnimeLightEntry { IsAnime = false }; curr.Id = int.Parse(links[0].Attributes["href"].Value.Split('/')[2]); var img = links[0].Descendants("img").First().Attributes["src"].Value; if (!img.Contains("questionmark")) { img = Regex.Replace(img, @"\/r\/\d+x\d+", ""); curr.ImgUrl = img.Substring(0, img.IndexOf('?')); } curr.Title = WebUtility.HtmlDecode(links[1].InnerText.Trim()); output.Mangaography.Add(curr); } } } var image = leftColumn.Descendants("img").First(); if (image.Attributes.Contains("alt")) { output.ImgUrl = image.Attributes["src"].Value; } output.Name = WebUtility.HtmlDecode(doc.DocumentNode.Descendants("h1").First().InnerText).Trim().Replace(" "," "); //because mal tends to leave two spaces there and there's pretty hardcore guy on github who can spot such things... props ;d output.Content = output.SpoilerContent = ""; output.Content += WebUtility.HtmlDecode(leftColumn.LastChild.InnerText.Trim()) + "\n\n"; foreach (var node in columns[1].ChildNodes) { if (node.Name == "#text") output.Content += WebUtility.HtmlDecode(node.InnerText.Trim()); else if (node.Name == "br" && !output.Content.EndsWith("\n\n")) output.Content += "\n"; else if (node.Name == "div" && node.Attributes.Contains("class") && node.Attributes["class"].Value == "spoiler") output.SpoilerContent += WebUtility.HtmlDecode(node.InnerText.Trim()) + "\n\n"; else if (node.Name == "table") { foreach (var descendant in node.Descendants("tr")) { var current = new AnimeStaffPerson(); var img = descendant.Descendants("img").First(); var imgUrl = img.Attributes["src"].Value; if (!imgUrl.Contains("questionmark")) { var pos = imgUrl.LastIndexOf("v"); if (pos != -1) imgUrl = imgUrl.Remove(pos, 1); } current.ImgUrl = imgUrl; var info = descendant.Descendants("td").Last(); current.Id = info.ChildNodes[0].Attributes["href"].Value.Split('/')[2]; current.Name = WebUtility.HtmlDecode(info.ChildNodes[0].InnerText.Trim()); current.Notes = info.ChildNodes[2].InnerText; output.VoiceActors.Add(current); } } } output.Content = output.Content.Trim(); output.SpoilerContent = output.SpoilerContent.Trim(); } catch (Exception) { //html } DataCache.SaveData(output, _id.ToString(), "character_details"); return output; }