@@ -5,16 +5,11 @@
using System.Text;
using System.Threading.Tasks;

namespace MALParser.Dto
namespace MALParser.Dto.AnimePageModels
{
public class EpisodesPageData
public class EpisodesPageData : BaseAnimeInfoActor
{
public BaseAnimeInfo BaseAnimeInfo { get; set; }

public EpisodesPageData(BaseAnimeInfo baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
public EpisodesPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public List<EpisodeInfo> Episodes { get; set; } = new List<EpisodeInfo>();

@@ -29,31 +24,31 @@ public async Task<EpisodesPageData> ParseNextPageAsync()
if (NextPageLink == null)
throw new Exception("Cannot parse next page because it does not exist!");

return await MALParser.AnimePage.Episodes.ParseAsync(NextPageLink.Path);
return await AnimePage.Episodes.ParseAsync(NextPageLink.Path);
}

public EpisodesPageData ParseNextPage()
{
if (NextPageLink == null)
throw new Exception("Cannot parse next page because it does not exist!");

return MALParser.AnimePage.Episodes.Parse(NextPageLink.Path);
return AnimePage.Episodes.Parse(NextPageLink.Path);
}

public async Task<EpisodesPageData> ParsePreviousPageAsync()
{
if (PreviousPageLink == null)
throw new Exception("Cannot parse previous page because it does not exist!");

return await MALParser.AnimePage.Episodes.ParseAsync(PreviousPageLink.Path);
return await AnimePage.Episodes.ParseAsync(PreviousPageLink.Path);
}

public EpisodesPageData ParsePreviousPage()
{
if (PreviousPageLink == null)
throw new Exception("Cannot parse previous page because it does not exist!");

return MALParser.AnimePage.Episodes.Parse(PreviousPageLink.Path);
return AnimePage.Episodes.Parse(PreviousPageLink.Path);
}
}

@@ -5,25 +5,21 @@
using System.Threading.Tasks;
using MALParser.Dto.Utility;

namespace MALParser.Dto
namespace MALParser.Dto.AnimePageModels
{
public class NewsPageData
public class NewsPageData : BaseAnimeInfoActor
{
public BaseAnimeInfo BaseAnimeInfo { get; set; }

public NewsPageData(BaseAnimeInfo baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
public NewsPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public List<NewsInfo> News { get; set; }
}

public class NewsInfo
{
public LinkInfo NewsBy { get; set; }
public LinkInfo Title { get; set; }
public LinkInfo ImageLink { get; set; }
public PersonDescriptionInfo Description { get; set; }
public string Description { get; set; }
public DateTime DatePublished { get; set; }
public LinkInfo DiscussLink { get; set; }
}
@@ -0,0 +1,17 @@
using MALParser.Dto.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MALParser.Dto.AnimePageModels
{
public class PicturesPageData : BaseAnimeInfoActor
{
public PicturesPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public List<LinkInfo> Pictures { get; set; } = new List<LinkInfo>();

}
}
@@ -5,16 +5,11 @@
using System.Threading.Tasks;
using MALParser.Dto.Utility;

namespace MALParser.Dto
namespace MALParser.Dto.AnimePageModels
{
public class RecommendationsPageData
public class RecommendationsPageData : BaseAnimeInfoActor
{
public BaseAnimeInfo BaseAnimeInfo { get; set; }

public RecommendationsPageData(BaseAnimeInfo baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
public RecommendationsPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public List<RecommendationInfo> Recommendations { get; set; }
}
@@ -24,6 +19,12 @@ public class RecommendationInfo
public int RecommendedUsers { get; set; }
public LinkInfo RecommendationAnime { get; set; }
public LinkInfo AnimeImageLink { get; set; }
public List<PersonDescriptionInfo> Reviews { get; set; }
public List<DescriptionInfo> Reviews { get; set; }
}

public class DescriptionInfo
{
public string Description { get; set; }
public LinkInfo By { get; set; }
}
}
@@ -5,21 +5,14 @@
using System.Threading.Tasks;
using MALParser.Dto.Utility;

namespace MALParser.Dto
namespace MALParser.Dto.AnimePageModels
{
public class ReviewsPageData
public class ReviewsPageData : BaseAnimeInfoActor
{
public BaseAnimeInfo BaseAnimeInfo { get; set; }

public ReviewsPageData(BaseAnimeInfo baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
public ReviewsPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public List<ReviewInfo> Reviews { get; set; } = new List<ReviewInfo>();

//public int PageNumber { get; set; } = -1;

public bool NextPageAvailable { get { return NextPageLink != null; } }
public bool PreviousPageAvailable { get { return PreviousPageLink != null; } }

@@ -31,31 +24,31 @@ public async Task<ReviewsPageData> ParseNextPageAsync()
if (NextPageLink == null)
throw new Exception("Cannot parse next page because it does not exist!");

return await MALParser.AnimePage.Reviews.ParseAsync(NextPageLink.Path);
return await AnimePage.Reviews.ParseAsync(NextPageLink.Path);
}

public ReviewsPageData ParseNextPage()
{
if (NextPageLink == null)
throw new Exception("Cannot parse next page because it does not exist!");

return MALParser.AnimePage.Reviews.Parse(NextPageLink.Path);
return AnimePage.Reviews.Parse(NextPageLink.Path);
}

public async Task<ReviewsPageData> ParsePreviousPageAsync()
{
if (PreviousPageLink == null)
throw new Exception("Cannot parse previous page because it does not exist!");

return await MALParser.AnimePage.Reviews.ParseAsync(PreviousPageLink.Path);
return await AnimePage.Reviews.ParseAsync(PreviousPageLink.Path);
}

public ReviewsPageData ParsePreviousPage()
{
if (PreviousPageLink == null)
throw new Exception("Cannot parse previous page because it does not exist!");

return MALParser.AnimePage.Reviews.Parse(PreviousPageLink.Path);
return AnimePage.Reviews.Parse(PreviousPageLink.Path);
}
}

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MALParser.Dto.Utility;

namespace MALParser.Dto.AnimePageModels
{
public class StatsPageData : BaseAnimeInfoActor
{
public StatsPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

public Dictionary<AnimeStatsType, long> SummaryStats { get; set; } = new Dictionary<AnimeStatsType, long>();

public Dictionary<int, long> ScoreStats { get; set; } = new Dictionary<int, long>();
}
}
@@ -5,20 +5,16 @@
using System.Text;
using System.Threading.Tasks;

namespace MALParser.Dto
namespace MALParser.Dto.AnimePageModels
{
public class VideosPageData
public class VideosPageData : BaseAnimeInfoActor
{
public BaseAnimeInfo BaseAnimeInfo { get; set; }

public VideosPageData(BaseAnimeInfo baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
public VideosPageData(AnimePageHeader baseAnimeInfo) : base(baseAnimeInfo) { }

/// <summary>
/// THIS DOESN'T WORK, refer to parsing episode pages instead.
/// </summary>
[Obsolete]
public List<LinkInfo> Episodes { get; set; } = new List<LinkInfo>();
public List<LinkInfo> Promotions { get; set; } = new List<LinkInfo>();
}
@@ -0,0 +1,35 @@
using MALParser.Dto.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MALParser.Dto
{
public class BaseAnimeInfo
{
//Links area

//Main Info
public string Title { get; set; }
public LinkInfo ImageLink { get; set; }
public string EnglishTitle { get; set; }
public string Type { get; set; } //Should add enum to this..
public string Episodes { get; set; }
public string Status { get; set; } //Should add enum to this..
public string Aired { get; set; }
public string Premiered { get; set; }
public string Broadcast { get; set; }
public string Synopsis { get; set; }

//Usually only 1 studio, yet to find a link with two
public List<LinkInfo> Studios { get; set; } = new List<LinkInfo>();
public SourceType Source { get; set; }
public List<string> Genres { get; set; } = new List<string>();

//Statistics
public float Score { get; set; } = -1;
public int Members { get; set; } = -1;
}
}

This file was deleted.

This file was deleted.

This file was deleted.

@@ -0,0 +1,19 @@
using MALParser.Dto.AnimePageModels;
using MALParser.Dto.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MALParser.Dto.Utility
{
public class BaseAnimeInfoActor
{
public AnimePageHeader BaseAnimeInfo { get; set; }
public BaseAnimeInfoActor(AnimePageHeader baseAnimeInfo)
{
this.BaseAnimeInfo = baseAnimeInfo;
}
}
}

This file was deleted.

@@ -10,11 +10,17 @@ public enum SourceType
{
Anime,
Manga,
Visualnovel,
Lightnovel,
VisualNovel,
LightNovel,
Original,
Novel,
Webmanga,
WebManga,
DigitalManga,
Game,
Music,
komaManga,
CardGame,
Other,
}

public enum FieldName
@@ -47,6 +53,7 @@ public enum RelatedAnime
{
Adaptation,
Alternativeversion,
Alternativesetting,
Sidestory,
Spinoff,
Otherlinks,
@@ -59,7 +66,7 @@ public enum RelatedAnime
None
}

public enum AnimeSummaryStats
public enum AnimeStatsType
{
Watching,
Completed,
@@ -52,26 +52,29 @@
<Compile Include="AnimePage\Pictures.cs" />
<Compile Include="AnimePage\Reviews.cs" />
<Compile Include="AnimePage\Stats.cs" />
<Compile Include="AnimePage\Utility.cs" />
<Compile Include="Utility.cs" />
<Compile Include="AnimePage\Videos.cs" />
<Compile Include="Dto\DetailsPageData.cs" />
<Compile Include="Dto\EpisodesPageData.cs" />
<Compile Include="Dto\PicturesPageData.cs" />
<Compile Include="Dto\Utility\BaseAnimeInfo.cs" />
<Compile Include="Dto\NewsPageData.cs" />
<Compile Include="Dto\Utility\PersonDescriptionInfo.cs" />
<Compile Include="Dto\CharactersPageData.cs" />
<Compile Include="Dto\AnimePageModels\AnimePageHeader.cs" />
<Compile Include="Dto\AnimePageModels\DetailsPageData.cs" />
<Compile Include="Dto\AnimePageModels\EpisodesPageData.cs" />
<Compile Include="Dto\Utility\BaseAnimeInfoActor.cs" />
<Compile Include="Dto\AnimePageModels\PicturesPageData.cs" />
<Compile Include="Dto\BaseAnimeInfo.cs" />
<Compile Include="Dto\AnimePageModels\NewsPageData.cs" />
<Compile Include="Dto\AnimePageModels\CharactersPageData.cs" />
<Compile Include="Dto\Utility\LinkInfo.cs" />
<Compile Include="Dto\RecommendationsPageData.cs" />
<Compile Include="Dto\ReviewsPageData.cs" />
<Compile Include="Dto\StatsPageData.cs" />
<Compile Include="Dto\VideosPageData.cs" />
<Compile Include="Dto\AnimePageModels\RecommendationsPageData.cs" />
<Compile Include="Dto\AnimePageModels\ReviewsPageData.cs" />
<Compile Include="Dto\AnimePageModels\StatsPageData.cs" />
<Compile Include="Dto\AnimePageModels\VideosPageData.cs" />
<Compile Include="Enums.cs" />
<Compile Include="ParserManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SeasonalAnime.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,77 @@
using HtmlAgilityPack;
using MALParser.Dto.AnimePageModels;
using MALParser.Dto.Utility;
using MALParser.Dto;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace MALParser
{
public static class SeasonalAnime
{
private static HttpClient client = new HttpClient();
public static async Task<List<BaseAnimeInfo>> ParseAsync(string link)
{
return AnalyzeDocument(await client.GetStringAsync(link));
}

public static List<BaseAnimeInfo> Parse(string link)
{
return AnalyzeDocument(client.GetStringAsync(link).Result);
}

private static List<BaseAnimeInfo> AnalyzeDocument(string HTMLCode)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(HTMLCode);

List<BaseAnimeInfo> list = new List<BaseAnimeInfo>();

try
{
foreach (var animeNode in doc.DocumentNode.Descendants("div").Where(x => x.GetAttributeValue("class", "") == "seasonal-anime js-seasonal-anime"))
{
BaseAnimeInfo page = new BaseAnimeInfo();
page.Title = Utility.FixString(HtmlEntity.DeEntitize(animeNode.Descendants("p").First(x => x.GetAttributeValue("class", "") == "title-text").InnerText));
page.Episodes = Utility.FixString(HtmlEntity.DeEntitize(animeNode.Descendants("div").First(x => x.GetAttributeValue("class", "") == "eps").InnerText));
if (animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "source").InnerText.Length > 1)
page.Source = (SourceType)Enum.Parse(typeof(SourceType), Utility.FixEnum(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "source").InnerText));
LinkInfo link = new LinkInfo();
link.Name = Utility.FixString(HtmlEntity.DeEntitize(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "producer").InnerText));
if(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "producer").Descendants("a").Any())
link.Path = Utility.GetCorrectLinkFormat(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "producer").Descendants("a").First().GetAttributeValue("href", ""));
page.Studios.Add(link);
animeNode.Descendants("span").Where(x => x.GetAttributeValue("span", "") == "genre").ToList().ForEach(x =>
{
page.Genres.Add(Utility.FixString(x.InnerText));
});
page.ImageLink = new LinkInfo(animeNode.Descendants("div").First(x => x.GetAttributeValue("class", "") == "image").Descendants().First().GetAttributeValue("src", ""));
page.Synopsis = HtmlEntity.DeEntitize(animeNode.Descendants("div").First(x => x.GetAttributeValue("class", "") == "synopsis js-synopsis").InnerText);
float score = -1;
float.TryParse(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "score").InnerText, out score);
page.Members = Utility.GetIntFromString(animeNode.Descendants("span").First(x => x.GetAttributeValue("class", "") == "member fl-r").InnerText);

string[] str = HtmlEntity.DeEntitize(animeNode.Descendants("div").First(x => x.GetAttributeValue("class", "") == "info").InnerText).Split('-');

page.Type = Utility.FixString(str[0].Replace(" ", ""));
page.Aired = Utility.FixString(str[1].Split(',').Take(2).Aggregate((x, y) => x + ", " + y));
if (str[1].Split(',').Length > 2)
page.Broadcast = Utility.FixString(str[1].Split(',')[2]);

list.Add(page);

}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + ex.StackTrace);
}

return list;
}
}
}
@@ -5,15 +5,14 @@
using System.Text;
using System.Threading.Tasks;

namespace MALParser.AnimePage
namespace MALParser
{
//TODO: make it internal.
internal class Utility
{
internal static int GetIntFromString(string text)
{
int number = 0;
for(int i = 0; i < text.Length; i++)
for (int i = 0; i < text.Length; i++)
{
if (char.IsDigit(text[i]))
{
@@ -24,7 +23,7 @@ internal static int GetIntFromString(string text)
return number;
}

internal static FieldName Classify(string text)
internal static FieldName ClassifyFieldName(string text)
{
if (!text.Contains(":"))
return FieldName.None;
@@ -44,6 +43,24 @@ internal static string GetCorrectLinkFormat(string wrongFormat)
{
return ""; //Should get the main link in here, and return the extra, but w/e
}
}
}

internal static string FixEnum(string text)
{
if (text.Contains(" "))
{
int captIndex = text.IndexOf(' ') + 1;
StringBuilder builder = new StringBuilder(text);
builder[captIndex] = (char)((builder[captIndex] - 'a') + 'A');
text = builder.ToString();
}
Console.WriteLine(text);
return new String(text.Where(char.IsLetter).ToArray());
}

internal static string FixString(string text)
{
return text.Replace("\n", "").Trim();
}
}
}
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -1,6 +1,7 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MALParser.Dto;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -3,6 +3,7 @@
using MALParser.Dto;
using System.Linq;
using MALParser.Dto.Utility;
using MALParser.Dto.AnimePageModels;

namespace UnitTesting.Anime
{
@@ -15,9 +16,7 @@ public void AnimeVideosPage_ParsedCorrectly()
string testLink = "https://myanimelist.net/anime/5114/Fullmetal_Alchemist__Brotherhood/video";
VideosPageData page = MALParser.AnimePage.Videos.Parse(testLink);

//TODO: Parse episodes
page.Episodes = Enumerable.Repeat(new LinkInfo(), 64).ToList();
Assert.AreEqual(page.Episodes.Count, 64);

Assert.AreEqual(page.Promotions.Count, 2);
}
}