static void Main(string[] args) { // MalApi uses the Common.Logging logging abstraction. // You can hook it up to any logging library that has a Common.Logging adapter. // See App.config for an example of hooking up MalApi to NLog. // Note that you will also need the appropriate NLog and Common.Logging.NLogXX packages installed. // Hooking up logging is not necessary but can be useful. // With the configuration in this example and with this example program, you will see lines like: // Logged from MalApi: Getting anime list for MAL user LordHighCaptain using URI https://myanimelist.net/malappinfo.php?status=all&type=anime&u=LordHighCaptain // Logged from MalApi: Successfully retrieved anime list for user LordHighCaptain using (MyAnimeListApi api = new MyAnimeListApi()) { api.UserAgent = "MalApiExample"; api.TimeoutInMs = 15000; var animeUpdateInfo = new AnimeUpdate() { Episode = 26, Status = AnimeCompletionStatus.Completed, Score = 9, }; string userUpdateAnime = api.UpdateAnimeForUser(1, animeUpdateInfo, "user", "password"); var mangaUpdateInfo = new MangaUpdate() { Chapter = 20, Volume = 3, Score = 8, Status = MangaCompletionStatus.Completed }; string userUpdateManga = api.UpdateMangaForUser(952, mangaUpdateInfo, "user", "password"); MalUserLookupResults userLookup = api.GetAnimeListForUser("user"); foreach (MyAnimeListEntry listEntry in userLookup.AnimeList) { Console.WriteLine("Rating for {0}: {1}", listEntry.AnimeInfo.Title, listEntry.Score); } Console.WriteLine(); Console.WriteLine(); RecentUsersResults recentUsersResults = api.GetRecentOnlineUsers(); foreach (string user in recentUsersResults.RecentUsers) { Console.WriteLine("Recent user: {0}", user); } Console.WriteLine(); Console.WriteLine(); int eurekaSevenID = 237; AnimeDetailsResults eurekaSeven = api.GetAnimeDetails(eurekaSevenID); Console.WriteLine("Eureka Seven genres: {0}", string.Join(", ", eurekaSeven.Genres)); } }
/// <summary> /// Push user details to MAL. /// This method automatically figures out if the anime should be added to the user's list or if it should simply be updated /// </summary> /// <param name="details">Update details</param> /// <param name="username">Username for authentication</param> /// <param name="password">Password for authentication</param> /// <returns>True - Update succeeded, otherwise false</returns> public async Task <DataPushResponseWrapper> PushAnimeDetailsToMal(AnimeUpdate details, string username, string password) { var userlist = await _listRetrievalWorker.RetrieveUserListAsync(username); var item = userlist.Anime.FirstOrDefault(t => t.SeriesId == details.AnimeId); return(item == null ? await UpdateAnimeDetails(details, username, password) : await UpdateAnimeDetails(details, username, password, true)); }
public async Task <AnimeUpdate> GetUserAnimeDetailsAsync(string username, int animeId, CancellationToken cancellationToken) { string url = string.Format(UserAnimeDetailsUri, animeId); HttpRequestMessage request = InitNewRequest(url, HttpMethod.Get); AnimeUpdate results = await ProcessRequestAsync(request, ScrapeUserAnimeDetailsFromHtml, cancellationToken : cancellationToken, baseErrorMessage : string.Format("Failed getting user anime details for user {0} anime ID {1}.", username, animeId)) .ConfigureAwait(continueOnCapturedContext: false); return(results); }
public void TestScrapeUserAnimeDetailsFromHtml() { string html; using (StreamReader reader = Helpers.GetResourceStream("Cowboy_Bebop.htm")) { html = reader.ReadToEnd(); } AnimeUpdate info = new AnimeUpdate() { Episode = 26, Status = AnimeCompletionStatus.Completed, Score = 8, StorageType = 5, // VHS StorageValue = 123, TimesRewatched = 2, RewatchValue = 4, // high DateStart = new DateTime(2017, 12, 10), DateFinish = new DateTime(2017, 12, 15), Priority = 1, // medium EnableDiscussion = 1, EnableRewatching = 1, Comments = "test updated comment, test updated comment2", Tags = "test updated tag, test updated tag 2" }; using (MyAnimeListApi api = new MyAnimeListApi()) { AnimeUpdate results = MalDetailScrapingUtils.ScrapeUserAnimeDetailsFromHtml(html); Assert.Equal(info.Episode, results.Episode); Assert.Equal(info.Status, results.Status); Assert.Equal(info.Score, results.Score); Assert.Equal(info.StorageType, results.StorageType); Assert.Equal(info.StorageValue, results.StorageValue); Assert.Equal(info.TimesRewatched, results.TimesRewatched); Assert.Equal(info.RewatchValue, results.RewatchValue); Assert.Equal(info.DateStart, results.DateStart); Assert.Equal(info.DateFinish, results.DateFinish); Assert.Equal(info.Priority, results.Priority); Assert.Equal(info.EnableDiscussion, results.EnableDiscussion); Assert.Equal(info.EnableRewatching, results.EnableRewatching); Assert.Equal(info.Comments, results.Comments); Assert.Equal(info.Tags, results.Tags); } }
public async Task <IEnumerable <AnimeUpdate> > GetAnimeUpdatesFromPage(int pageNumber) { if (pageNumber <= 0) { throw new ArgumentOutOfRangeException(nameof(pageNumber), pageNumber, "Page number cannot be less or equal to 0"); } var animeUpdates = new List <AnimeUpdate>(); var updateNodes = (await GetLastUpdateNodes(pageNumber)); foreach (var node in updateNodes) { var nodeInnerText = node.QuerySelector(".update-info").InnerText; var episodeNums = Regex.Split(nodeInnerText, @"\D+").Where(num => !string.IsNullOrEmpty(num)).ToList(); if (episodeNums.Count == 0) { continue; } var animePageSrc = $"https://yummyanime.com{node.Attributes.FirstOrDefault(a => a.Name == "href").Value}"; var anime = new Anime { Name = node.QuerySelector(".update-title").InnerText, PageSrc = animePageSrc }; var publisher = new Publisher { Name = Regex.Replace(nodeInnerText, @"[^a-zA-Z]", "") }; var updateDate = node.QuerySelector(".update-date").InnerText; DubType dubType = nodeInnerText.Contains("озвучкой") ? DubType.Voiceover : DubType.Subtitiles; var num1 = int.Parse(episodeNums[0]); var num2 = int.Parse(episodeNums.ElementAtOrDefault(1) ?? episodeNums[0]); for (int i = num1; i <= num2; i++) { var animeUpdate = new AnimeUpdate { Anime = anime, Publisher = publisher, EpisodeNum = i, UpdateDate = updateDate, DubType = dubType }; animeUpdates.Add(animeUpdate); } } return(animeUpdates); }
/// <summary> /// Push update/add details to MAL /// </summary> /// <param name="details">Update details</param> /// <param name="username">Username for authentication</param> /// <param name="password">Password for authentication</param> /// <param name="isupdate">Indicate if this is an update or an add</param> /// <returns>True - Update succeeded, otherwise false</returns> private async Task <DataPushResponseWrapper> UpdateAnimeDetails(AnimeUpdate details, string username, string password, bool isupdate = false) { try { var url = isupdate ? MalRouteBuilder.UpdateAnime(details.AnimeId) : MalRouteBuilder.AddAnime(details.AnimeId); var client = _httpClientFactory.GetHttpClient(username, password); var result = await client.PostAsync(url, new FormUrlEncodedContent(new[] { new KeyValuePair <string, string>("data", _xmlHelper.SerializeData(details)) })); return(new DataPushResponseWrapper(result.StatusCode, result.IsSuccessStatusCode)); } catch (Exception exception) { return(new DataPushResponseWrapper(exception)); } }
public static AnimeUpdate ScrapeUserAnimeDetailsFromHtml(string userAnimeDetailsHtml) { AnimeUpdate results = new AnimeUpdate(); var doc = new HtmlDocument(); doc.LoadHtml(userAnimeDetailsHtml); // Episode results.Episode = doc.GetElementbyId("add_anime_num_watched_episodes").GetAttributeValue("value", -1); // Status var parentNode = doc.GetElementbyId("add_anime_status"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { results.Status = (AnimeCompletionStatus)childNode.GetAttributeValue("value", -1); break; } } // Enable rewatching results.EnableRewatching = doc.GetElementbyId("add_anime_is_rewatching").Attributes["checked"] == null ? 0 : 1; // Score parentNode = doc.GetElementbyId("add_anime_score"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { results.Score = childNode.GetAttributeValue("value", -1); break; } } // Start date int day = -1; int month = -1; int year = -1; parentNode = doc.GetElementbyId("add_anime_start_date_month"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { month = childNode.GetAttributeValue("value", -1); break; } } parentNode = doc.GetElementbyId("add_anime_start_date_day"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { day = childNode.GetAttributeValue("value", -1); break; } } parentNode = doc.GetElementbyId("add_anime_start_date_year"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { year = childNode.GetAttributeValue("value", -1); break; } } if (month == -1 || day == -1 || year == -1) { results.DateStart = null; } else { results.DateStart = new DateTime(year, month, day); } // Date finish parentNode = doc.GetElementbyId("add_anime_finish_date_month"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { month = childNode.GetAttributeValue("value", -1); break; } } parentNode = doc.GetElementbyId("add_anime_finish_date_day"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { day = childNode.GetAttributeValue("value", -1); break; } } parentNode = doc.GetElementbyId("add_anime_finish_date_year"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { year = childNode.GetAttributeValue("value", -1); break; } } if (month == -1 || day == -1 || year == -1) { results.DateFinish = null; } else { results.DateFinish = new DateTime(year, month, day); } // Tags results.Tags = doc.GetElementbyId("add_anime_tags").InnerText; // Priority parentNode = doc.GetElementbyId("add_anime_priority"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { results.Priority = childNode.GetAttributeValue("value", -1); break; } } // Storage type parentNode = doc.GetElementbyId("add_anime_storage_type"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { results.StorageType = childNode.GetAttributeValue("value", -1); break; } } // Storage value results.StorageValue = doc.GetElementbyId("add_anime_storage_value").GetAttributeValue("value", -1); // Times rewatched results.TimesRewatched = doc.GetElementbyId("add_anime_num_watched_times").GetAttributeValue("value", -1); // Rewatch value parentNode = doc.GetElementbyId("add_anime_rewatch_value"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { results.RewatchValue = childNode.GetAttributeValue("value", -1); break; } } // Comments results.Comments = doc.GetElementbyId("add_anime_comments").InnerText; // Enable discussion parentNode = doc.GetElementbyId("add_anime_is_asked_to_discuss"); foreach (var childNode in parentNode.ChildNodes) { if (childNode.Attributes["selected"] != null) { // Because 'Enable discussion = 1' sent for update sets the first options of the dropdown, which corresponds to 0 and vice versa... int temp = childNode.GetAttributeValue("value", -1); temp = temp == 1 ? 0 : 1; results.EnableDiscussion = temp; break; } } return(results); }