Added MangaDex Api. Updated project structure.

This commit is contained in:
2025-05-26 17:16:25 -04:00
parent 648aa95f32
commit ea8b4a36ff
61 changed files with 4937 additions and 197 deletions

View File

@@ -11,6 +11,8 @@
<ItemGroup>
<None Remove="Search\MangaDex\SampleSearchResult.json" />
<None Remove="WebCrawlers\MangaDex\MetadataSample-Feed.json" />
<None Remove="WebCrawlers\MangaDex\MetadataSample.json" />
<None Remove="WebCrawlers\Samples\MangaNato - Please Go Home, Akutsu-San!.htm" />
<None Remove="Search\NatoManga\SampleSearchResult.json" />
</ItemGroup>
@@ -27,6 +29,8 @@
<ItemGroup>
<EmbeddedResource Include="Search\MangaDex\SampleSearchResult.json" />
<EmbeddedResource Include="Search\NatoManga\SampleSearchResult.json" />
<EmbeddedResource Include="WebCrawlers\MangaDex\MetadataSample-Feed.json" />
<EmbeddedResource Include="WebCrawlers\MangaDex\MetadataSample.json" />
</ItemGroup>
<ItemGroup>

View File

@@ -36,7 +36,7 @@ public class MangaDexSearchTests
IHttpService httpService = Substitute.For<IHttpService>();
httpService.GetStringAsync(Arg.Any<string>())
httpService.GetStringAsync(Arg.Any<string>(), CancellationToken.None)
.Returns(Task.FromResult(searchResultJson));
MangaDexSearchProvider searchProvider = new(httpService);

View File

@@ -1,6 +1,6 @@
using MangaReader.Core.HttpService;
using MangaReader.Core.Search;
using MangaReader.Core.Search.NatoManga;
using MangaReader.Core.Sources.NatoManga.Search;
using MangaReader.Tests.Utilities;
using NSubstitute;
using Shouldly;
@@ -31,7 +31,7 @@ public class NatoMangaWebSearchTests
IHttpService httpService = Substitute.For<IHttpService>();
httpService.GetStringAsync(Arg.Any<string>())
httpService.GetStringAsync(Arg.Any<string>(), CancellationToken.None)
.Returns(Task.FromResult(searchResultJson));
NatoMangaSearchProvider searchProvider = new(httpService);

View File

@@ -0,0 +1,51 @@
using MangaReader.Core.HttpService;
using MangaReader.Core.Sources.MangaDex.Api;
using MangaReader.Tests.Utilities;
using NSubstitute;
using Shouldly;
namespace MangaReader.Tests.WebCrawlers.MangaDex;
public class MangaDexMetadataTests
{
[Fact]
public async Task Get_Manga_Metadata()
{
string resourceName = "MangaReader.Tests.WebCrawlers.MangaDex.MetadataSample.json";
string searchResultJson = await ResourceHelper.ReadJsonResourceAsync(resourceName);
IHttpService httpService = Substitute.For<IHttpService>();
httpService.GetStringAsync(Arg.Any<string>(), CancellationToken.None)
.Returns(Task.FromResult(searchResultJson));
MangaDexClient mangaDexClient = new(httpService);
MangaDexResponse mangaDexResponse = await mangaDexClient.GetMangaAsync(Guid.NewGuid(), CancellationToken.None);
mangaDexResponse.Response.ShouldBe("entity");
mangaDexResponse.ShouldBeOfType<MangaDexEntityResponse>();
MangaDexEntityResponse? mangaDexEntityResponse = mangaDexResponse as MangaDexEntityResponse;
mangaDexEntityResponse.ShouldNotBeNull();
mangaDexEntityResponse.Data.ShouldNotBeNull();
mangaDexEntityResponse.Data.ShouldBeOfType<MangaEntity>();
MangaEntity? mangaEntity = mangaDexEntityResponse.Data as MangaEntity;
mangaEntity.ShouldNotBeNull();
mangaEntity.Attributes.Title.ShouldContainKey("en");
mangaEntity.Attributes.Title["en"].ShouldBe("Gals Cant Be Kind to Otaku!?");
mangaEntity.Attributes.Description.ShouldContainKey("en");
mangaEntity.Attributes.Description["en"].ShouldBe("Takuya Seo is an otaku who likes \"anime for girls\" and can't say he likes it out loud. One day, he hooks up with two gals from his class, Amane and Ijichi, but it seems that Amane is also an otaku...");
mangaEntity.Attributes.Tags.Count.ShouldBe(5);
mangaEntity.Attributes.Tags[0].Attributes.Name.ShouldContainKey("en");
mangaEntity.Attributes.Tags[0].Attributes.Name["en"].ShouldBe("Romance");
mangaEntity.Attributes.Tags[1].Attributes.Name.ShouldContainKey("en");
mangaEntity.Attributes.Tags[1].Attributes.Name["en"].ShouldBe("Comedy");
mangaEntity.Attributes.Tags[2].Attributes.Name.ShouldContainKey("en");
mangaEntity.Attributes.Tags[2].Attributes.Name["en"].ShouldBe("School Life");
mangaEntity.Attributes.Tags[3].Attributes.Name.ShouldContainKey("en");
mangaEntity.Attributes.Tags[3].Attributes.Name["en"].ShouldBe("Slice of Life");
mangaEntity.Attributes.Tags[4].Attributes.Name.ShouldContainKey("en");
mangaEntity.Attributes.Tags[4].Attributes.Name["en"].ShouldBe("Gyaru");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
{
"result": "ok",
"response": "entity",
"data": {
"id": "ee96e2b7-9af2-4864-9656-649f4d3b6fec",
"type": "manga",
"attributes": {
"title": { "en": "Gals Can\u2019t Be Kind to Otaku!?" },
"altTitles": [
{ "ja": "\u30aa\u30bf\u30af\u306b\u512a\u3057\u3044\u30ae\u30e3\u30eb\u306f\u3044\u306a\u3044\uff01\uff1f" },
{ "ja-ro": "Otaku ni Yasashii Gal wa Inai!?" },
{ "ja-ro": "Otaku ni Yasashii Gyaru ha Inai!?" },
{ "en": "Gal Can\u0027t Be Kind to Otaku!?" },
{ "en": "Gals Can\u0027t Be Kind To A Geek!?" },
{ "zh": "\u6ca1\u6709\u8fa3\u59b9\u4f1a\u5bf9\u963f\u5b85\u6e29\u67d4!?" },
{ "pt-br": "Gals N\u00e3o Podem ser Gentis com Otakus!?" },
{ "es-la": "\u00bf\u00a1Las Gals no pueden ser amables con los Otakus!?" },
{ "vi": "Gyaru kh\u00f4ng th\u1ec3 t\u1eed t\u1ebf v\u1edbi Otaku \u01b0?" }
],
"description": {
"en": "Takuya Seo is an otaku who likes \u0022anime for girls\u0022 and can\u0027t say he likes it out loud. One day, he hooks up with two gals from his class, Amane and Ijichi, but it seems that Amane is also an otaku...",
"ja": "\u3042\u307e\u308a\u5927\u304d\u306a\u58f0\u3067\u597d\u304d\u3068\u8a00\u3048\u306a\u3044\u201c\u5973\u5150\u5411\u3051\u30a2\u30cb\u30e1\u201d\u304c\u597d\u304d\u306a\u30aa\u30bf\u30af \u702c\u5c3e\u5353\u4e5f\u3002\u3042\u308b\u65e5\u3001\u30af\u30e9\u30b9\u306e\u30ae\u30e3\u30eb \u5929\u97f3\u3055\u3093\u3068\u4f0a\u5730\u77e5\u3055\u3093\u306b\u7d61\u307e\u308c\u305f\u306e\u3060\u304c\u3001\u4f55\u3084\u3089\u5929\u97f3\u3055\u3093\u3082\u30aa\u30bf\u30af\u306e\u5302\u3044\u304c\u2026\uff1f",
"vi": "M\u1ed9t anh b\u1ea1n trung b\u00ecnh Otaku th\u00edch anime d\u00e0nh cho b\u00e9 g\u00e1i, gi\u00e1p m\u1eb7t hai n\u00e0ng Gyaru xinh \u0111\u1eb9p n\u1ed5i ti\u1ebfng nh\u1ea5t tr\u01b0\u1eddng. Nh\u01b0ng kh\u00f4ng ch\u1ec9 c\u00f3 th\u1ebf, m\u1ed9t trong hai n\u1eef nh\u00e2n xinh \u0111\u1eb9p \u1ea5y c\u00f3 g\u00ec \u0111\u00f3...h\u01a1i otaku th\u00ec ph\u1ea3i...?",
"es-la": "Takuya Seo es un otaku al que le gusta el \u0022anime para chicas\u0022 y no puede decir que le guste en voz alta. Un d\u00eda, se junta con dos chicas de su clase, Amane e Ijichi, pero parece que Amane tambi\u00e9n es una otaku...",
"pt-br": "Takuya Seo \u00e9 um otaku que gosta de \u0022animes para garotinhas\u0022 e n\u00e3o pode dizer isso em voz alta. Um dia, ele conversa com duas gals da sua sala, Amane e Ijichi, mas parece que a Amane tamb\u00e9m \u00e9 uma otaku... Uma com\u00e9dia rom\u00e2ntica escolar onde o otaku conhece as gals que ele gosta!"
},
"isLocked": false,
"links": {
"al": "138380",
"ap": "otaku-ni-yasashii-gal-wa-inai",
"bw": "series\/339484",
"kt": "69614",
"mu": "188325",
"amz": "https:\/\/www.amazon.co.jp\/dp\/B0BB2R5WVF",
"cdj": "https:\/\/www.cdjapan.co.jp\/searchuni?q=\u30aa\u30bf\u30af\u306b\u512a\u3057\u3044\u30ae\u30e3\u30eb\u306f\u3044\u306a\u3044\uff01\uff1f+(Zenon+Comics)\u0026order=relasc",
"ebj": "https:\/\/ebookjapan.yahoo.co.jp\/books\/690493\/",
"mal": "144152",
"raw": "https:\/\/comic-zenon.com\/episode\/3269754496560134267",
"engtl": "https:\/\/x.com\/yenpress\/status\/1913348424826581290"
},
"originalLanguage": "ja",
"lastVolume": "",
"lastChapter": "",
"publicationDemographic": "seinen",
"status": "ongoing",
"year": 2021,
"contentRating": "safe",
"tags": [
{
"id": "423e2eae-a7a2-4a8b-ac03-a8351462d71d",
"type": "tag",
"attributes": {
"name": { "en": "Romance" },
"description": {},
"group": "genre",
"version": 1
},
"relationships": []
},
{
"id": "4d32cc48-9f00-4cca-9b5a-a839f0764984",
"type": "tag",
"attributes": {
"name": { "en": "Comedy" },
"description": {},
"group": "genre",
"version": 1
},
"relationships": []
},
{
"id": "caaa44eb-cd40-4177-b930-79d3ef2afe87",
"type": "tag",
"attributes": {
"name": { "en": "School Life" },
"description": {},
"group": "theme",
"version": 1
},
"relationships": []
},
{
"id": "e5301a23-ebd9-49dd-a0cb-2add944c7fe9",
"type": "tag",
"attributes": {
"name": { "en": "Slice of Life" },
"description": {},
"group": "genre",
"version": 1
},
"relationships": []
},
{
"id": "fad12b5e-68ba-460e-b933-9ae8318f5b65",
"type": "tag",
"attributes": {
"name": { "en": "Gyaru" },
"description": {},
"group": "theme",
"version": 1
},
"relationships": []
}
],
"state": "published",
"chapterNumbersResetOnNewVolume": false,
"createdAt": "2022-03-08T07:41:47+00:00",
"updatedAt": "2025-04-19T23:05:17+00:00",
"version": 44,
"availableTranslatedLanguages": [ "it", "es-la", "en", "id", "fr", "vi", "pt-br" ],
"latestUploadedChapter": "9e8f9776-a8bf-4118-83df-f0d086479d64"
},
"relationships": [
{
"id": "518965e7-c26c-4fd9-881c-f7ce0e78323d",
"type": "author",
"attributes": {
"name": "Norishiro-chan",
"imageUrl": null,
"biography": { "en": "Name (in native language)\n\u306e\u308a\u3057\u308d\u3061\u3083\u3093" },
"twitter": "https:\/\/twitter.com\/norishirochan",
"pixiv": "https:\/\/www.pixiv.net\/en\/users\/821541",
"melonBook": null,
"fanBox": null,
"booth": null,
"namicomi": null,
"nicoVideo": null,
"skeb": null,
"fantia": null,
"tumblr": null,
"youtube": null,
"weibo": null,
"naver": null,
"website": null,
"createdAt": "2021-10-10T03:41:31+00:00",
"updatedAt": "2024-08-13T17:35:37+00:00",
"version": 4
}
},
{
"id": "767b8851-6060-478a-a23e-f819fec0fbf2",
"type": "artist",
"attributes": {
"name": "Sakana Uozimi",
"imageUrl": null,
"biography": { "en": "Name (in native language)\n\u9b5a\u4f4f\u3055\u304b\u306a" },
"twitter": "https:\/\/twitter.com\/namekyunta",
"pixiv": "https:\/\/www.pixiv.net\/en\/users\/468508",
"melonBook": null,
"fanBox": null,
"booth": null,
"namicomi": null,
"nicoVideo": null,
"skeb": null,
"fantia": null,
"tumblr": null,
"youtube": null,
"weibo": null,
"naver": null,
"website": "https:\/\/galleria.emotionflow.com\/11070\/",
"createdAt": "2022-03-08T07:36:27+00:00",
"updatedAt": "2023-11-27T23:10:41+00:00",
"version": 3
}
},
{
"id": "a06943fd-6309-49a8-a66a-8df0f6dc41eb",
"type": "cover_art",
"attributes": {
"description": "Volume 9 Cover from BookLive",
"volume": "9",
"fileName": "6b3073de-bb65-4723-8113-6068bf8c6eb4.jpg",
"locale": "ja",
"createdAt": "2025-02-20T11:59:45+00:00",
"updatedAt": "2025-02-20T11:59:45+00:00",
"version": 1
}
},
{
"id": "6c0a87bf-3adb-4854-a4a6-d5c358b73d21",
"type": "creator"
}
]
}
}

View File

@@ -1,4 +1,4 @@
using MangaReader.Core.WebCrawlers.NatoManga;
using MangaReader.Core.Sources.NatoManga.Metadata;
using Shouldly;
namespace MangaReader.Tests.WebCrawlers.NatoManga;

View File

@@ -1,5 +1,5 @@
using MangaReader.Core.WebCrawlers;
using MangaReader.Core.WebCrawlers.MangaNato;
using MangaReader.Core.Metadata;
using MangaReader.Core.Sources.MangaNato.Metadata;
using Shouldly;
namespace MangaReader.Tests.WebCrawlers;