From c9e4c8cb6a2ef9cadbd4c3f1cdd9aa9ac691d43b Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 00:54:50 +0100 Subject: [PATCH 01/56] Remove unwanted code --- BooruSharp.Others/BooruSharp.Others.csproj | 5 +- .../BooruSharp.UnitTests.csproj | 12 +- BooruSharp.UnitTests/BooruTestData.cs | 9 + BooruSharp.UnitTests/BooruTests.cs | 818 +----------------- BooruSharp.UnitTests/Boorus.cs | 51 -- BooruSharp.UnitTests/General.cs | 155 ---- BooruSharp.UnitTests/OtherTests.cs | 51 -- BooruSharp/Booru/ABooru.cs | 19 +- BooruSharp/Booru/Furrybooru.cs | 4 +- BooruSharp/Booru/UrlFormat.cs | 2 +- BooruSharp/BooruSharp.csproj | 7 +- 11 files changed, 46 insertions(+), 1087 deletions(-) create mode 100644 BooruSharp.UnitTests/BooruTestData.cs delete mode 100644 BooruSharp.UnitTests/Boorus.cs delete mode 100644 BooruSharp.UnitTests/General.cs delete mode 100644 BooruSharp.UnitTests/OtherTests.cs diff --git a/BooruSharp.Others/BooruSharp.Others.csproj b/BooruSharp.Others/BooruSharp.Others.csproj index 38bba8d..9a6e008 100644 --- a/BooruSharp.Others/BooruSharp.Others.csproj +++ b/BooruSharp.Others/BooruSharp.Others.csproj @@ -1,9 +1,9 @@  - netstandard2.0 + net5.0 true - 2.2.1 + 3.0.0 Xwilarg Xwilarg Extension of BooruSharp to handle non-booru websites and handle them like the others @@ -23,7 +23,6 @@ - diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index a08bc60..c2e3bb4 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -1,7 +1,7 @@  - net5.0 + net7.0 true @@ -11,18 +11,8 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/BooruSharp.UnitTests/BooruTestData.cs b/BooruSharp.UnitTests/BooruTestData.cs new file mode 100644 index 0000000..09f0e9d --- /dev/null +++ b/BooruSharp.UnitTests/BooruTestData.cs @@ -0,0 +1,9 @@ +using System; + +namespace BooruSharp.UnitTests +{ + public record BooruTestData + { + public Type BooruType; + } +} diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index d455171..25fb418 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,808 +1,32 @@ using BooruSharp.Booru; using BooruSharp.Others; -using System; using System.Collections.Generic; -using System.Net.Http; -using System.Threading.Tasks; -using Xunit; namespace BooruSharp.UnitTests { public class BooruTests { - private const int _randomPostCount = 5; - - public static IEnumerable BooruParams { get; } = new object[][] - { - new object[] { typeof(Atfbooru) }, - new object[] { typeof(DanbooruDonmai) }, - new object[] { typeof(E621) }, - new object[] { typeof(E926) }, - new object[] { typeof(Derpibooru) }, - //new object[] { typeof(Furrybooru) }, - new object[] { typeof(Gelbooru) }, - new object[] { typeof(Konachan) }, - new object[] { typeof(Lolibooru) }, - new object[] { typeof(Ponybooru) }, - new object[] { typeof(Realbooru) }, - new object[] { typeof(Rule34) }, - new object[] { typeof(Safebooru) }, - new object[] { typeof(Sakugabooru) }, - new object[] { typeof(SankakuComplex) }, - new object[] { typeof(Twibooru) }, - new object[] { typeof(Xbooru) }, - new object[] { typeof(Yandere) }, - new object[] { typeof(Pixiv) }, - }; - - public static IEnumerable BooruPostCountParams { get; } = new object[][] - { - new object[] { typeof(Atfbooru) }, - new object[] { typeof(DanbooruDonmai), "hibiki_(kancolle)" }, - new object[] { typeof(Derpibooru), "swimsuit", "hat" }, - new object[] { typeof(E621), "kantai_collection", "swimwear" }, - new object[] { typeof(E926), "kantai_collection", "swimwear" }, - //new object[] { typeof(Furrybooru), "kantai_collection" }, - new object[] { typeof(Gelbooru), "hibiki_(kancolle)" }, - new object[] { typeof(Konachan), "hibiki_(kancolle)" }, - new object[] { typeof(Lolibooru) }, - new object[] { typeof(Ponybooru), "swimsuit", "hat" }, - new object[] { typeof(Realbooru), "swimsuit", "asian" }, - new object[] { typeof(Rule34) }, - new object[] { typeof(Safebooru) }, - new object[] { typeof(Sakugabooru), "kantai_collection", "explosions" }, - new object[] { typeof(SankakuComplex) }, - new object[] { typeof(Twibooru), "swimsuit", "hat" }, - new object[] { typeof(Xbooru), "kantai_collection" }, - new object[] { typeof(Yandere), "kantai_collection", "swimsuits" }, - new object[] { typeof(Pixiv), "響(艦隊これくしょん)", "水着艦娘" }, - }; - - public static IEnumerable BooruRandomPostsParams { get; } = new object[][] - { - new object[] { typeof(Atfbooru) }, - new object[] { typeof(DanbooruDonmai) }, - new object[] { typeof(Derpibooru), "swimsuit" }, - new object[] { typeof(E621) }, - new object[] { typeof(E926) }, - //new object[] { typeof(Furrybooru) }, - new object[] { typeof(Gelbooru) }, - new object[] { typeof(Konachan) }, - new object[] { typeof(Lolibooru) }, - new object[] { typeof(Ponybooru), "swimsuit" }, - new object[] { typeof(Realbooru), "small_breasts" }, - new object[] { typeof(Rule34) }, - new object[] { typeof(Safebooru) }, - new object[] { typeof(Sakugabooru), "kantai_collection" }, - new object[] { typeof(SankakuComplex), "small_breasts" }, - new object[] { typeof(Twibooru), "swimsuit" }, - new object[] { typeof(Xbooru) }, - new object[] { typeof(Yandere) }, - new object[] { typeof(Pixiv), "スク水" }, - }; - - public static IEnumerable BooruRandomTwoTagsParams { get; } = new object[][] - { - new object[] { typeof(Atfbooru) }, - new object[] { typeof(DanbooruDonmai), "hibiki_(kancolle)" }, - new object[] { typeof(Derpibooru), "swimsuit", "hat" }, - new object[] { typeof(E621), "kantai_collection" }, - new object[] { typeof(E926), "kantai_collection" }, - //new object[] { typeof(Furrybooru), "kantai_collection" }, - new object[] { typeof(Gelbooru), "hibiki_(kancolle)" }, - new object[] { typeof(Konachan), "hibiki_(kancolle)" }, - new object[] { typeof(Lolibooru) }, - new object[] { typeof(Ponybooru), "swimsuit", "hat" }, - new object[] { typeof(Realbooru), "school_swimsuit", "small_breasts" }, - new object[] { typeof(Rule34) }, - new object[] { typeof(Safebooru) }, - new object[] { typeof(Sakugabooru), "kantai_collection", "explosions" }, - new object[] { typeof(SankakuComplex), "hibiki_(kantai_collection)", "old_school_swimsuit" }, - new object[] { typeof(Twibooru), "swimsuit", "hat" }, - new object[] { typeof(Xbooru), "kantai_collection" }, - new object[] { typeof(Yandere), "kantai_collection" }, - new object[] { typeof(Pixiv), "響(艦隊これくしょん)", "スク水" }, - }; - - public static IEnumerable BooruTooManyTagsParams { get; } = new object[][] - { - new object[] { typeof(Atfbooru), false }, - new object[] { typeof(DanbooruDonmai), true }, - new object[] { typeof(Derpibooru), false, "swimsuit", "hat", "necklace" }, - new object[] { typeof(E621), false, "sea", "loli", "swimwear" }, - new object[] { typeof(E926), false, "sea", "breasts", "swimwear" }, - //new object[] { typeof(Furrybooru), false, "water" }, - new object[] { typeof(Gelbooru), false }, - new object[] { typeof(Konachan), false, "water" }, - new object[] { typeof(Lolibooru), false }, - new object[] { typeof(Ponybooru), false, "swimsuit", "hat", "necklace" }, - new object[] { typeof(Realbooru), false, "water" }, - new object[] { typeof(Rule34), false }, - new object[] { typeof(Safebooru), false }, - new object[] { typeof(Sakugabooru), false, "kantai_collection", "explosions", "fire" }, - new object[] { typeof(SankakuComplex), false, "ocean", "loli", "swimsuit" }, - new object[] { typeof(Twibooru), false, "swimsuit", "hat", "necklace" }, - new object[] { typeof(Xbooru), false, "ocean", "small_breasts" }, - new object[] { typeof(Yandere), false, "see_through", "loli", "swimsuits" }, - new object[] { typeof(Pixiv), false, "東方", "貧乳", "水着" }, + public static IEnumerable BooruParams { get; } = new[] + { + new BooruTestData() { BooruType = typeof(Atfbooru) }, + new BooruTestData() { BooruType = typeof(DanbooruDonmai) }, + new BooruTestData() { BooruType = typeof(Derpibooru) }, + new BooruTestData() { BooruType = typeof(E621) }, + new BooruTestData() { BooruType = typeof(E926) }, + new BooruTestData() { BooruType = typeof(Furrybooru) }, + new BooruTestData() { BooruType = typeof(Gelbooru) }, + new BooruTestData() { BooruType = typeof(Konachan) }, + new BooruTestData() { BooruType = typeof(Lolibooru) }, + new BooruTestData() { BooruType = typeof(Ponybooru) }, + new BooruTestData() { BooruType = typeof(Realbooru) }, + new BooruTestData() { BooruType = typeof(Rule34) }, + new BooruTestData() { BooruType = typeof(Safebooru) }, + new BooruTestData() { BooruType = typeof(Sakugabooru) }, + new BooruTestData() { BooruType = typeof(SankakuComplex) }, + new BooruTestData() { BooruType = typeof(Twibooru) }, + new BooruTestData() { BooruType = typeof(Xbooru) }, + new BooruTestData() { BooruType = typeof(Yandere) }, + new BooruTestData() { BooruType = typeof(Pixiv) } }; - - [Fact] - public void IsBooruAuthSet() - { - var b = new Gelbooru(); - Assert.True(b.Auth == null); - b.Auth = new BooruAuth("AAA", "AAA"); - Assert.False(b.Auth == null); - } - - [SkippableFact] - public async Task GetPixivBookmarksAsync() - { - var booru = (Pixiv)await Boorus.GetAsync(typeof(Pixiv)); - - var result = await booru.GetFavoritesAsync(56850985); - - Assert.NotEmpty(result); - - foreach (var r in result) - { - Assert.NotNull(r.FileUrl); - } - } - - [SkippableFact] - public async Task GetPixivBookmarksInvalidIdAsync() - { - var booru = (Pixiv)await Boorus.GetAsync(typeof(Pixiv)); - - var result = await booru.GetFavoritesAsync(111); - - Assert.Empty(result); - } - - [Fact] - public async Task E621PostWithAuth() - { - var booru = await Boorus.GetAsync(); - General.Authorize(booru); - - Skip.If(booru is Xbooru, "Xbooru allows adding a post with invalid ID."); - - var res = await booru.GetRandomPostAsync("cub", "pussy"); - Assert.True(res.FileUrl != null); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task UnsetFavoriteErrorAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - var id = (await General.GetRandomPostAsync(booru)).ID; - booru.Auth = new BooruAuth("AAA", "AAA"); - - if (!booru.HasFavoriteAPI) - await Assert.ThrowsAsync(() => booru.RemoveFavoriteAsync(id)); - - if (booru is Gelbooru) - await Assert.ThrowsAsync(() => booru.RemoveFavoriteAsync(id)); - } - - [Theory] - [MemberData(nameof(BooruParams))] - public async Task SetFavoriteErrorAsync(Type t) - { - const int invalidPostId = 800; - - var booru = (ABooru)Activator.CreateInstance(t); - booru.Auth = new BooruAuth("AAA", "AAA"); - if (booru is Pixiv pixiv) - await Assert.ThrowsAsync(() => pixiv.AddFavoriteAsync(invalidPostId)); - else if (!booru.HasFavoriteAPI) - await Assert.ThrowsAsync(() => booru.AddFavoriteAsync(invalidPostId)); - else - await Assert.ThrowsAsync(() => booru.AddFavoriteAsync(invalidPostId)); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task SetFavoriteInvalidIdAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - - Skip.If(booru is Xbooru, "Xbooru allows adding a post with invalid ID."); - - if (!booru.HasFavoriteAPI) - await Assert.ThrowsAsync(() => booru.AddFavoriteAsync(int.MaxValue)); - else - { - // Pixiv doesn't support authorization using Auth property. - if (!(booru is Pixiv)) - { - General.Authorize(booru); - } - - await Assert.ThrowsAsync(() => booru.AddFavoriteAsync(int.MaxValue)); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task SetFavoriteAsync(Type t) - { - const int postID = 10; - var booru = await Boorus.GetAsync(t); - - if (!booru.HasFavoriteAPI) - await Assert.ThrowsAsync(() => booru.AddFavoriteAsync(postID)); - else - { - var id = (await General.GetRandomPostAsync(booru)).ID; - - // Pixiv doesn't support authorization using Auth property. - if (!(booru is Pixiv)) - { - General.Authorize(booru); - } - - await booru.AddFavoriteAsync(id); - await booru.RemoveFavoriteAsync(id); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetByMd5Async(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasPostByMd5API) - await Assert.ThrowsAsync(() => booru.GetPostByMd5Async("0")); - else - { - Search.Post.SearchResult result1; - do - { - result1 = await General.GetRandomPostAsync(booru); - } while (result1.MD5 == null); - var result2 = await booru.GetPostByMd5Async(result1.MD5); - Assert.Equal(result1.ID, result2.ID); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetByIdAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasPostByIdAPI) - await Assert.ThrowsAsync(() => booru.GetPostByIdAsync(0)); - else - { - Search.Post.SearchResult result1 = await General.GetRandomPostAsync(booru); - var result2 = await booru.GetPostByIdAsync(result1.ID); - Assert.Equal(result1.ID, result2.ID); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetLastPostsAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (booru.NoEmptyPostSearch) - await Assert.ThrowsAsync(() => booru.GetLastPostsAsync()); - else - { - var results = await booru.GetLastPostsAsync(); - Assert.NotInRange(results.Length, 0, 1); - Assert.NotEqual(results[0].ID, results[1].ID); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetLastPostsWithLimitAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (booru.NoEmptyPostSearch) - await Assert.ThrowsAsync(() => booru.GetLastPostsAsync()); - else - { - var results = await booru.GetLastPostsAsync(100); - Assert.Equal(100, results.Length); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruPostCountParams))] - public async Task GetLastPostsWithTagsAsync(Type t, string tag = "hibiki_(kantai_collection)", string tag2 = "swimsuit") - { - var booru = await Boorus.GetAsync(t); - Search.Post.SearchResult[] results; - results = await booru.GetLastPostsAsync(tag, tag2); - Assert.NotInRange(results.Length, 0, 1); - Assert.NotEqual(results[0].ID, results[1].ID); - foreach (var elem in results) - { - Assert.Contains(elem.Tags, x => x.Contains(tag)); - Assert.Contains(elem.Tags, x => x.Contains(tag2)); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruPostCountParams))] - public async Task GetPostCountAsync(Type t, string tag = "hibiki_(kantai_collection)", string tag2 = "swimsuit") - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasPostCountAPI) - await Assert.ThrowsAsync(() => booru.GetPostCountAsync()); - else - { - int countEmpty = booru.NoEmptyPostSearch - ? int.MaxValue - // Pixiv doesn't handle PostCount with no tag - : await booru.GetPostCountAsync(); - var countOne = await booru.GetPostCountAsync(tag); - var countTwo = await booru.GetPostCountAsync(tag, tag2); - Assert.NotEqual(0, countEmpty); - Assert.NotEqual(0, countOne); - Assert.NotEqual(0, countTwo); - Assert.InRange(countOne, countTwo, countEmpty); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruRandomPostsParams))] - public async Task GetRandomAsync(Type t, string tag = "school_swimsuit") - { - await General.CheckGetRandomAsync(await Boorus.GetAsync(t), tag); - } - - [SkippableTheory] - [MemberData(nameof(BooruRandomPostsParams))] - public async Task GetRandomsAsync(Type t, string tag = "school_swimsuit") - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasMultipleRandomAPI) - await Assert.ThrowsAsync(() => General.CheckGetRandomsAsync(booru, tag)); - else - await General.CheckGetRandomsAsync(booru, tag); - } - - [SkippableTheory] - [MemberData(nameof(BooruRandomPostsParams))] - public async Task GetRandomsTooManyAsync(Type t, string tag = "school_swimsuit") - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasMultipleRandomAPI) - await Assert.ThrowsAsync(() => booru.GetRandomPostsAsync(int.MaxValue, tag)); - else - { - var result = await booru.GetRandomPostsAsync(int.MaxValue, tag); - Assert.NotEmpty(result); - foreach (var r in result) - Assert.Contains(r.Tags, x => x.Contains(tag)); - } - } - - [Fact] - public async Task SetHttpClientAsync() - { - var booru = new Gelbooru(); - HttpClient hc = new HttpClient(); - hc.DefaultRequestHeaders.Add("User-Agent", "BooruSharp.Unit-Tests"); - booru.HttpClient = hc; - await General.CheckGetRandomAsync(booru, "kantai_collection"); - Assert.Single(hc.DefaultRequestHeaders.GetValues("User-Agent")); - Assert.Contains("BooruSharp.Unit-Tests", hc.DefaultRequestHeaders.GetValues("User-Agent")); - } - - [SkippableTheory] - [MemberData(nameof(BooruRandomTwoTagsParams))] - public async Task GetRandom2TagsAsync(Type t, string tag = "hibiki_(kantai_collection)", string tag2 = "school_swimsuit") - { - var booru = await Boorus.GetAsync(t); - if (t.IsAssignableFrom(typeof(DanbooruDonmai))) - { - await Assert.ThrowsAsync(() => booru.GetRandomPostAsync(tag, tag2)); - } - else - { - var result = await booru.GetRandomPostAsync(tag, tag2); - Assert.Contains(result.Tags, x => x.Contains(tag)); - Assert.Contains(result.Tags, x => x.Contains(tag2)); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruRandomTwoTagsParams))] - public async Task GetRandoms2TagsAsync(Type t, string tag = "hibiki_(kantai_collection)", string tag2 = "school_swimsuit") - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasMultipleRandomAPI) - await Assert.ThrowsAsync(() => booru.GetRandomPostsAsync(_randomPostCount, tag, tag2)); - else if (t.IsAssignableFrom(typeof(DanbooruDonmai))) - { - await Assert.ThrowsAsync(() => booru.GetRandomPostsAsync(_randomPostCount, tag, tag2)); - } - else - { - var result = await booru.GetRandomPostsAsync(_randomPostCount, tag, tag2); - Assert.NotEmpty(result); - foreach (var r in result) - { - Assert.Contains(r.Tags, x => x.Contains(tag)); - Assert.Contains(r.Tags, x => x.Contains(tag2)); - } - } - } - - [SkippableTheory] - [MemberData(nameof(BooruTooManyTagsParams))] - public async Task TooManyTagsAsync( - Type t, bool throwError, string tag = "ocean", string tag2 = "flat_chest", string tag3 = "swimsuit") - { - var booru = await Boorus.GetAsync(t); - Search.Post.SearchResult result; - if (throwError) - { - await Assert.ThrowsAsync(async () => - { - result = await booru.GetRandomPostAsync(tag, tag2, tag3); - }); - } - else - { - result = await booru.GetRandomPostAsync(tag, tag2, tag3); - Assert.Contains(result.Tags, x => x.Contains(tag)); - Assert.Contains(result.Tags, x => x.Contains(tag2)); - Assert.Contains(result.Tags, x => x.Contains(tag3)); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruTooManyTagsParams))] - public async Task TooManyTagsManyAsync( - Type t, bool throwError, string tag = "ocean", string tag2 = "flat_chest", string tag3 = "swimsuit") - { - var booru = await Boorus.GetAsync(t); - Search.Post.SearchResult[] result; - if (throwError) - { - await Assert.ThrowsAsync(async () => - { - result = await booru.GetRandomPostsAsync(_randomPostCount, tag, tag2, tag3); - }); - } - else if (!booru.HasMultipleRandomAPI) - await Assert.ThrowsAsync( - () => booru.GetRandomPostsAsync(_randomPostCount, tag, tag2, tag3)); - else - { - result = await booru.GetRandomPostsAsync(_randomPostCount, tag, tag2, tag3); - foreach (var r in result) - { - Assert.Contains(tag, r.Tags); - Assert.Contains(tag2, r.Tags); - Assert.Contains(tag3, r.Tags); - } - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetRandomFailAsync(Type t) - { - await Assert.ThrowsAsync( - async () => await (await Boorus.GetAsync(t)).GetRandomPostAsync("someInvalidTag")); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetRandomsFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasMultipleRandomAPI) - await Assert.ThrowsAsync( - () => booru.GetRandomPostsAsync(_randomPostCount, "someInvalidTag")); - else - Assert.Empty(await booru.GetRandomPostsAsync(_randomPostCount, "someInvalidTag")); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru))] - [InlineData(typeof(DanbooruDonmai))] - [InlineData(typeof(E621))] - [InlineData(typeof(E926))] - //[InlineData(typeof(Furrybooru))] - [InlineData(typeof(Gelbooru))] - [InlineData(typeof(Gelbooru), "'o'ne")] - [InlineData(typeof(Konachan))] - [InlineData(typeof(Lolibooru))] - [InlineData(typeof(Realbooru))] - [InlineData(typeof(Rule34))] - [InlineData(typeof(Safebooru))] - [InlineData(typeof(Sakugabooru), "kantai_collection")] - [InlineData(typeof(SankakuComplex))] - [InlineData(typeof(Xbooru))] - [InlineData(typeof(Yandere))] - [InlineData(typeof(Pixiv), "パンスト")] - [InlineData(typeof(Derpibooru))] - [InlineData(typeof(Ponybooru))] - [InlineData(typeof(Twibooru))] - public async Task CheckTagAsync(Type t, string tag = "pantyhose") - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasTagByIdAPI) - await Assert.ThrowsAsync(() => booru.GetTagAsync(tag)); - else - await General.CheckTagAsync(booru, tag); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckTagFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasTagByIdAPI) - await Assert.ThrowsAsync(() => booru.GetTagAsync("someRandomTag")); - else - await Assert.ThrowsAsync(() => booru.GetTagAsync("someRandomTag")); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru), "female", true)] - [InlineData(typeof(DanbooruDonmai), "hibi", true)] - [InlineData(typeof(E621), "hibiki", true)] - [InlineData(typeof(E926), "hibiki", true)] - //[InlineData(typeof(Furrybooru), "hibiki", true)] - [InlineData(typeof(Gelbooru), "hibiki", true)] - [InlineData(typeof(Konachan), "hibiki", false)] - [InlineData(typeof(Lolibooru), "hibiki", false)] - [InlineData(typeof(Realbooru), "female", true)] - [InlineData(typeof(Rule34), "hibiki", true)] - [InlineData(typeof(Safebooru), "hibiki", true)] - [InlineData(typeof(Sakugabooru), "kantai", false)] - [InlineData(typeof(SankakuComplex), "hibiki", false)] - [InlineData(typeof(Xbooru), "hibiki", true)] - [InlineData(typeof(Yandere), "hibiki", false)] - [InlineData(typeof(Pixiv), "艦隊こ", false)] - [InlineData(typeof(Derpibooru), "swi", false)] - [InlineData(typeof(Ponybooru), "swi", false)] - [InlineData(typeof(Twibooru), "swi", false)] - public async Task CheckTagsAsync(Type t, string tag, bool onlyOnce) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasTagByIdAPI) - await Assert.ThrowsAsync(() => booru.GetTagsAsync(tag)); - else if (onlyOnce) - Assert.NotEmpty(await booru.GetTagsAsync(tag)); - else - Assert.NotInRange((await booru.GetTagsAsync(tag)).Length, 0, 1); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru), "hibiki_(kantai_collection)", 2033)] - [InlineData(typeof(DanbooruDonmai), "hibiki_(kancolle)", 1275718)] - [InlineData(typeof(E621), "kantai_collection", 267881)] - [InlineData(typeof(E926), "kantai_collection", 1329650)] - //[InlineData(typeof(Furrybooru), "kantai_collection", 151628)] - [InlineData(typeof(Gelbooru), "hibiki_(kantai_collection)", 463392)] - [InlineData(typeof(Konachan), "hibiki_(kancolle)", 75885)] - [InlineData(typeof(Lolibooru), "hibiki_(kantai_collection)", 2939)] - [InlineData(typeof(Realbooru), "kantai_collection", 688290)] - [InlineData(typeof(Rule34), "hibiki_(kantai_collection)", 321239)] - [InlineData(typeof(Safebooru), "hibiki_(kantai_collection)", 316679)] - [InlineData(typeof(Sakugabooru), "kantai_collection", 7148)] - [InlineData(typeof(SankakuComplex), "kantai_collection", 458437)] - [InlineData(typeof(Xbooru), "hibiki_(kantai_collection)", 151883)] - [InlineData(typeof(Yandere), "hibiki_(kancolle)", 98153)] - [InlineData(typeof(Pixiv), "響(艦隊これくしょん)", -1)] - [InlineData(typeof(Derpibooru), "swimsuit", 43858)] - [InlineData(typeof(Ponybooru), "swimsuit", 1299)] - [InlineData(typeof(Twibooru), "swimsuit", 2998)] - public async Task TagIdAsync(Type t, string tag, int tagId) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasTagByIdAPI) - await Assert.ThrowsAsync(() => booru.GetTagAsync(tagId)); - else - Assert.Equal(tag, (await booru.GetTagAsync(tagId)).Name); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task TagIdFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasTagByIdAPI) - await Assert.ThrowsAsync(() => booru.GetTagAsync(int.MaxValue)); - else - await Assert.ThrowsAsync(() => booru.GetTagAsync(int.MaxValue)); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru), "highres", 82)] - [InlineData(typeof(DanbooruDonmai), "futanari", 3589)] - [InlineData(typeof(E621), "futanari", 123)] - [InlineData(typeof(E926), "futanari", 123)] - //[InlineData(typeof(Furrybooru), "futanari", -1)] - [InlineData(typeof(Gelbooru), "futanari", -1)] - [InlineData(typeof(Konachan), "futanari", 757)] - [InlineData(typeof(Lolibooru), "futanari", 158)] - [InlineData(typeof(Realbooru), "futanari", -1)] - [InlineData(typeof(Rule34), "futanari", -1)] - [InlineData(typeof(Safebooru), "futanari", -1)] - [InlineData(typeof(Sakugabooru), "animated", 13)] - [InlineData(typeof(SankakuComplex), "blush", 826)] - [InlineData(typeof(Xbooru), "futanari", -1)] - [InlineData(typeof(Yandere), "futanari", 167)] - [InlineData(typeof(Pixiv), "ふたなり", -1)] - [InlineData(typeof(Derpibooru), "swimsuit", -1)] - [InlineData(typeof(Ponybooru), "swimsuit", -1)] - [InlineData(typeof(Twibooru), "swimsuit", -1)] - public async Task CheckWikiAsync(Type t, string tag, int? id) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasWikiAPI) - await Assert.ThrowsAsync(() => booru.GetWikiAsync(tag)); - else - { - Search.Wiki.SearchResult result = await booru.GetWikiAsync(tag); - Assert.Equal(id, result.ID); - General.CheckWiki(result); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckWikiFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasWikiAPI) - await Assert.ThrowsAsync(() => booru.GetWikiAsync("yetAnotherTag")); - else - await Assert.ThrowsAsync(() => booru.GetWikiAsync("yetAnotherTag")); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru), "kantai_collection", "anchor_symbol")] - [InlineData(typeof(DanbooruDonmai), "kantai_collection", "serafuku")] - [InlineData(typeof(E621), "sky", "cloud")] - [InlineData(typeof(E926), "sky", "cloud")] - //[InlineData(typeof(Furrybooru), "sky", "cloud")] - [InlineData(typeof(Gelbooru), "sky", "cloud")] - [InlineData(typeof(Konachan), "sky", "clouds")] - [InlineData(typeof(Lolibooru), "sky", "cloud")] - [InlineData(typeof(Realbooru), "sky", "clouds")] - [InlineData(typeof(Rule34), "sky", "clouds")] - [InlineData(typeof(Safebooru), "sky", "clouds")] - [InlineData(typeof(Sakugabooru), "kantai_collection", "explosions")] - [InlineData(typeof(SankakuComplex), "sky", "clouds")] - [InlineData(typeof(Xbooru), "sky", "clouds")] - [InlineData(typeof(Yandere), "landscape", "wallpaper")] - [InlineData(typeof(Pixiv), "空", "雲")] - [InlineData(typeof(Derpibooru), "swimsuit", "")] - [InlineData(typeof(Ponybooru), "swimsuit", "")] - [InlineData(typeof(Twibooru), "swimsuit", "")] - public async Task CheckRelatedAsync(Type t, string tag, string related) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasRelatedAPI) - await Assert.ThrowsAsync(() => booru.GetRelatedAsync(tag)); - else - { - Search.Related.SearchResult[] result = await booru.GetRelatedAsync(tag); - General.CheckRelated(result); - Assert.Contains(result, x => x.Name == related); - } - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckRelatedFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasRelatedAPI) - await Assert.ThrowsAsync(() => booru.GetRelatedAsync("thisWillFail")); - else - Assert.Empty(await booru.GetRelatedAsync("thisWillFail")); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru), 366473)] - [InlineData(typeof(DanbooruDonmai), 3193008)] - [InlineData(typeof(E621), 59432)] - [InlineData(typeof(E926), 541858)] - //[InlineData(typeof(Furrybooru), 1282210)] - [InlineData(typeof(Gelbooru), 3988284)] - [InlineData(typeof(Konachan), 142938)] - [InlineData(typeof(Lolibooru), 134097)] - [InlineData(typeof(Realbooru), 646911)] - [InlineData(typeof(Rule34), 2840746)] - [InlineData(typeof(Safebooru), 132)] - [InlineData(typeof(Sakugabooru), 38886)] - [InlineData(typeof(SankakuComplex), 48)] - [InlineData(typeof(Xbooru), 740157)] - [InlineData(typeof(Yandere), 619494)] - [InlineData(typeof(Pixiv), -1)] - [InlineData(typeof(Derpibooru), 1)] - [InlineData(typeof(Ponybooru), 1)] - [InlineData(typeof(Twibooru), 1)] - public async Task CheckCommentAsync(Type t, int id) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasCommentAPI) - await Assert.ThrowsAsync(() => booru.GetCommentsAsync(id)); - else - General.CheckComment(await booru.GetCommentsAsync(id)); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckCommentFailAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasCommentAPI) - await Assert.ThrowsAsync(() => booru.GetCommentsAsync(int.MaxValue)); - else - Assert.Empty(await booru.GetCommentsAsync(int.MaxValue)); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckLastCommentAsync(Type t) - { - var booru = await Boorus.GetAsync(t); - if (!booru.HasSearchLastComment) - await Assert.ThrowsAsync(() => booru.GetLastCommentsAsync()); - else - General.CheckComment(await booru.GetLastCommentsAsync()); - } - - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task CheckAvailableAsync(Type t) - { - await (await Boorus.GetAsync(t)).CheckAvailabilityAsync(); - } - - [SkippableTheory] - [InlineData(typeof(Atfbooru))] - [InlineData(typeof(DanbooruDonmai))] - [InlineData(typeof(E621))] - [InlineData(typeof(E926), "breast")] - //[InlineData(typeof(Furrybooru))] - [InlineData(typeof(Gelbooru))] - [InlineData(typeof(Konachan))] - [InlineData(typeof(Lolibooru))] - [InlineData(typeof(Realbooru))] - [InlineData(typeof(Rule34))] - [InlineData(typeof(Safebooru), "breast")] - [InlineData(typeof(Sakugabooru), "another")] - [InlineData(typeof(SankakuComplex), "pussy_line")] - [InlineData(typeof(Xbooru))] - [InlineData(typeof(Yandere))] - [InlineData(typeof(Pixiv), "おまんこ")] - [InlineData(typeof(Derpibooru))] - [InlineData(typeof(Ponybooru))] - [InlineData(typeof(Twibooru))] - public async Task CheckIsSafeAsync(Type t, string explicitTag = "pussy") - { - ABooru b = await Boorus.GetAsync(t); - bool isSafe = b.IsSafe; - bool foundExplicit = false; - for (int i = 0; i < 10; i++) - { - var image = await b.GetRandomPostAsync(explicitTag); - if (isSafe && image.FileUrl != null) - Assert.NotEqual(Search.Post.Rating.Explicit, image.Rating); - if (image.Rating == Search.Post.Rating.Explicit) - foundExplicit = true; - } - if (!isSafe) - Assert.True(foundExplicit); - - } } } diff --git a/BooruSharp.UnitTests/Boorus.cs b/BooruSharp.UnitTests/Boorus.cs deleted file mode 100644 index 0afc118..0000000 --- a/BooruSharp.UnitTests/Boorus.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using BooruSharp.Booru; -using BooruSharp.Others; -using Xunit; - -namespace BooruSharp.UnitTests -{ - internal static class Boorus - { - private static readonly Dictionary> _boorus = new Dictionary>(); - - - - public static Task GetAsync(Type type) - { - lock (_boorus) - { - if (!_boorus.TryGetValue(type, out var booruTask)) - { - booruTask = Task.Run(() => CreateBooruAsync(type)); - _boorus[type] = booruTask; - } - - return booruTask; - } - } - - public static Task GetAsync() where T : ABooru - { - return GetAsync(typeof(T)); - } - - private static async Task CreateBooruAsync(Type type) - { - var booru = (ABooru)Activator.CreateInstance(type); - - if (booru is Pixiv pixiv) - { - string refresh = Environment.GetEnvironmentVariable("PIXIV_REFRESH_TOKEN"); - - Skip.If(refresh == null, "Pixiv tokens aren't set."); - - await pixiv.LoginAsync(refresh); - } - - return booru; - } - } -} diff --git a/BooruSharp.UnitTests/General.cs b/BooruSharp.UnitTests/General.cs deleted file mode 100644 index 3b002d9..0000000 --- a/BooruSharp.UnitTests/General.cs +++ /dev/null @@ -1,155 +0,0 @@ -using BooruSharp.Booru; -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; -using Xunit; - -namespace BooruSharp.UnitTests -{ - public static class General - { - private const int _randomPostIterationCount = 10; - private const int _randomPostCount = 5; - private const Search.Tag.TagType _unknownTagType = (Search.Tag.TagType)2; - - private static async Task CheckUrlAsync(Uri url) - { - try - { - using (HttpClient hc = new HttpClient()) - { - hc.DefaultRequestHeaders.Add("User-Agent", "BooruSharp"); - await hc.SendAsync(new HttpRequestMessage(HttpMethod.Head, url)); - } - return null; - } - catch (WebException ex) - { - return ex.Message + " for " + url; - } - } - - public static async Task CheckResultAsync(Search.Post.SearchResult result, string inputTag) - { - if (result.FileUrl != null) - { - string resFile = await CheckUrlAsync(result.FileUrl); - string resPreview = await CheckUrlAsync(result.PreviewUrl); - string resPost = await CheckUrlAsync(result.PostUrl); - Assert.True(resPost == null, resPost); - Assert.True(resFile == null, resFile); - Assert.True(resPreview == null, resPreview); - Assert.NotEqual(0, result.Height); - Assert.NotEqual(0, result.Width); - if (result.PreviewHeight != null) - { - Assert.NotEqual(0, result.PreviewHeight); - Assert.NotEqual(0, result.PreviewWidth); - } - if(result.SampleUri != null) - { - string resSample = await CheckUrlAsync(result.SampleUri); - Assert.True(resSample == null, resSample); - } - } - Assert.InRange(result.Rating, Search.Post.Rating.Safe, Search.Post.Rating.Explicit); - Assert.Contains(result.Tags, t => t.Contains(inputTag)); - if(result.DetailedTags != null) - Assert.Contains(result.DetailedTags, t => t.Name.Contains(inputTag)); - Assert.NotEqual(0, result.ID); - if (result.Size.HasValue) - Assert.NotEqual(0, result.Size.Value); - } - - public static async Task CheckGetRandomAsync(ABooru booru, string s1) - { - Search.Post.SearchResult result = await booru.GetRandomPostAsync(s1); - Search.Post.SearchResult result2; - int i = 0; - do - { - result2 = await booru.GetRandomPostAsync(s1); - i++; - } while (result.ID == result2.ID && i < _randomPostIterationCount); - Assert.NotEqual(result.ID, result2.ID); - await CheckResultAsync(result, s1); - } - - public static async Task CheckGetRandomsAsync(ABooru booru, string s1) - { - Search.Post.SearchResult[] result = await booru.GetRandomPostsAsync(_randomPostCount, s1); - Assert.NotEmpty(result); - Search.Post.SearchResult[] result2; - int i = 0; - do - { - result2 = await booru.GetRandomPostsAsync(_randomPostCount, s1); - Assert.NotEmpty(result2); - i++; - } while (result[0].ID == result2[0].ID && i < _randomPostIterationCount); - Assert.NotEqual(result[0].ID, result2[0].ID); - await CheckResultAsync(result[0], s1); - } - - public static async Task CheckTagAsync(ABooru booru, string s1 = "pantyhose") - { - Search.Tag.SearchResult result = await booru.GetTagAsync(s1); - Assert.Equal(s1, result.Name); - Assert.InRange(result.Type, Search.Tag.TagType.Trivia, Search.Tag.TagType.Metadata); - Assert.NotEqual(_unknownTagType, result.Type); - Assert.NotEqual(0, result.Count); - } - - public static void CheckWiki(Search.Wiki.SearchResult result) - { - Assert.InRange(result.LastUpdate, result.Creation, DateTime.Now); - } - - public static void CheckRelated(Search.Related.SearchResult[] result) - { - Assert.NotEmpty(result); - } - - public static void CheckComment(Search.Comment.SearchResult[] result) - { - foreach (Search.Comment.SearchResult res in result) - { - Assert.NotEqual(0, res.AuthorID); - Assert.NotEqual(0, res.CommentID); - Assert.NotEqual(0, res.PostID); - } - Assert.NotEmpty(result); - } - - public static bool CompareArray(Search.Post.SearchResult[] res1, Search.Post.SearchResult[] res2) - { - if (res1.Length != res2.Length) - return false; - for (int i = 0; i < res1.Length; i++) - if (res1[i].ID != res2[i].ID) - return false; - return true; - } - - public static async Task GetRandomPostAsync(ABooru booru) - { - if (booru.NoEmptyPostSearch) - return await booru.GetRandomPostAsync("スク水"); // Pixiv doesn't handle random search with no tag - return await booru.GetRandomPostAsync(); - } - - public static void Authorize(ABooru booru) - { - string booruName = booru.GetType().Name.ToUpperInvariant(); - string userID = Environment.GetEnvironmentVariable(booruName + "_USER_ID"); - string passwordHash = Environment.GetEnvironmentVariable(booruName + "_PASSWORD_HASH"); - - Skip.If( - userID == null || passwordHash == null, - $"{booruName}_* environment variables aren't set."); - - booru.Auth = new BooruAuth(userID, passwordHash); - } - } -} diff --git a/BooruSharp.UnitTests/OtherTests.cs b/BooruSharp.UnitTests/OtherTests.cs deleted file mode 100644 index 7524b8a..0000000 --- a/BooruSharp.UnitTests/OtherTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -using BooruSharp.Booru; -using System.Threading.Tasks; -using Xunit; - -namespace BooruSharp.UnitTests -{ - // TODO: Find a way to check if commands are available on website or not - - public class OtherTests - {// TODO: Need to test these with others boorus - [Fact] - public async Task GelbooruTagCharacterAsync() - { - Assert.Equal( - Search.Tag.TagType.Character, - (await (await Boorus.GetAsync()).GetTagAsync("cirno")).Type); - } - - [Fact] - public async Task GelbooruTagCopyrightAsync() - { - Assert.Equal( - Search.Tag.TagType.Copyright, - (await (await Boorus.GetAsync()).GetTagAsync("kantai_collection")).Type); - } - - [Fact] - public async Task GelbooruTagArtistAsync() - { - Assert.Equal( - Search.Tag.TagType.Artist, - (await (await Boorus.GetAsync()).GetTagAsync("mtu_(orewamuzituda)")).Type); - } - - [Fact] - public async Task GelbooruTagMetadataAsync() - { - Assert.Equal( - Search.Tag.TagType.Metadata, - (await (await Boorus.GetAsync()).GetTagAsync("highres")).Type); - } - - [Fact] - public async Task GelbooruTagTriviaAsync() - { - Assert.Equal( - Search.Tag.TagType.Trivia, - (await (await Boorus.GetAsync()).GetTagAsync("futanari")).Type); - } - } -} diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 6fa9f2f..591804d 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,6 +1,4 @@ using BooruSharp.Search; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Linq; @@ -24,25 +22,22 @@ public abstract partial class ABooru /// public abstract bool IsSafe { get; } - private protected virtual Search.Comment.SearchResult GetCommentSearchResult(object json) + private protected virtual Search.Comment.SearchResult GetCommentSearchResult(T parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Post.SearchResult GetPostSearchResult(JToken obj) + private protected virtual Search.Post.SearchResult GetPostSearchResult(T parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Post.SearchResult[] GetPostsSearchResult(object json) + private protected virtual Search.Post.SearchResult[] GetPostsSearchResult(T parsingData) => throw new FeatureUnavailable(); - private protected virtual JToken ParseFirstPostSearchResult(object json) + private protected virtual Search.Related.SearchResult GetRelatedSearchResult(T parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Related.SearchResult GetRelatedSearchResult(object json) + private protected virtual Search.Tag.SearchResult GetTagSearchResult(T parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Tag.SearchResult GetTagSearchResult(object json) - => throw new FeatureUnavailable(); - - private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(object json) + private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(T parsingData) => throw new FeatureUnavailable(); private protected virtual async Task GetTagEnumerableSearchResultAsync(Uri url) @@ -279,7 +274,7 @@ private Task GetXmlAsync(Uri url) private async Task GetRandomIdAsync(string tags) { - HttpResponseMessage msg = await HttpClient.GetAsync(BaseUrl + "index.php?page=post&s=random&tags=" + tags); + HttpResponseMessage msg = await HttpClient.GetAsync(BaseUrl + "index.php?page=post&s=random&" + tags); msg.EnsureSuccessStatusCode(); return HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id"); } diff --git a/BooruSharp/Booru/Furrybooru.cs b/BooruSharp/Booru/Furrybooru.cs index 653fcb6..9229ec9 100644 --- a/BooruSharp/Booru/Furrybooru.cs +++ b/BooruSharp/Booru/Furrybooru.cs @@ -6,14 +6,14 @@ namespace BooruSharp.Booru /// FurryBooru. /// https://furry.booru.org/ /// - [Obsolete("Furrybooru does no longer works, please consider using E621/E926 instead", error: true)] // TODO: Looks like it's fixed + //[Obsolete("Furrybooru does no longer works, please consider using E621/E926 instead", error: true)] // TODO: Looks like it's fixed public sealed class Furrybooru : Template.Gelbooru02 { /// /// Initializes a new instance of the class. /// public Furrybooru() - : base("furry.booru.org", BooruOptions.UseHttp) + : base("furry.booru.org") { } /// diff --git a/BooruSharp/Booru/UrlFormat.cs b/BooruSharp/Booru/UrlFormat.cs index ab9ef00..6359738 100644 --- a/BooruSharp/Booru/UrlFormat.cs +++ b/BooruSharp/Booru/UrlFormat.cs @@ -15,7 +15,7 @@ public enum UrlFormat /// PostIndexJson, /// - /// Indicates that the API uses /index.php?page=dapi&s=post&q=index&json=1 query scheme. + /// Indicates that the API uses /index.php?page=dapi&s=post&q=index&json=1 query scheme. /// IndexPhp, /// diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index fb67c1d..81a3fe2 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -1,9 +1,9 @@  - netstandard2.0 + net5.0 true - 3.5.5 + 4.0.0 Xwilarg BooruSharp is a C# library to browse Booru websites (Gelbooru, Konachan, etc...) easily @@ -26,12 +26,11 @@ True - - + From 2c44014b875988acaa646e710a01bf9146b8736e Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 02:07:58 +0100 Subject: [PATCH 02/56] Comment code that isn't working --- BooruSharp.Others/Pixiv.cs | 2 -- BooruSharp/Booru/ABooru.cs | 18 ++++++++---------- BooruSharp/Booru/Lolibooru.cs | 8 ++++---- BooruSharp/Booru/Parsing/EmptyParsing.cs | 5 +++++ BooruSharp/Booru/Template/BooruOnRails.cs | 7 ++++--- BooruSharp/Booru/Template/Danbooru.cs | 7 ++++--- BooruSharp/Booru/Template/E621.cs | 7 +++++-- BooruSharp/Booru/Template/Gelbooru.cs | 9 +++++---- BooruSharp/Booru/Template/Gelbooru02.cs | 9 ++++++--- BooruSharp/Booru/Template/Moebooru.cs | 6 ++++-- BooruSharp/Booru/Template/Philomena.cs | 7 ++++--- BooruSharp/Booru/Template/Sankaku.cs | 6 ++++-- BooruSharp/BooruSharp.csproj | 4 ---- BooruSharp/Search/Comment/ABooru.cs | 6 +++--- BooruSharp/Search/Favorite/ABooru.cs | 2 ++ BooruSharp/Search/Post/ABooru.cs | 6 +++--- BooruSharp/Search/Related/ABooru.cs | 6 +++--- BooruSharp/Search/Tag/ABooru.cs | 6 +++--- BooruSharp/Search/Wiki/ABooru.cs | 6 +++--- 19 files changed, 70 insertions(+), 57 deletions(-) create mode 100644 BooruSharp/Booru/Parsing/EmptyParsing.cs diff --git a/BooruSharp.Others/Pixiv.cs b/BooruSharp.Others/Pixiv.cs index b7ceb72..9db804b 100644 --- a/BooruSharp.Others/Pixiv.cs +++ b/BooruSharp.Others/Pixiv.cs @@ -1,8 +1,6 @@ using BooruSharp.Booru; using BooruSharp.Search; using BooruSharp.Search.Post; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 591804d..0856aa8 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -14,7 +14,7 @@ namespace BooruSharp.Booru /// /// Defines basic capabilities of a booru. This class is . /// - public abstract partial class ABooru + public abstract partial class ABooru { /// /// Gets whether this booru is considered safe (that is, all posts on @@ -22,24 +22,22 @@ public abstract partial class ABooru /// public abstract bool IsSafe { get; } - private protected virtual Search.Comment.SearchResult GetCommentSearchResult(T parsingData) + private protected virtual Search.Comment.SearchResult GetCommentSearchResult(TComment parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Post.SearchResult GetPostSearchResult(T parsingData) + private protected virtual Search.Post.SearchResult GetPostSearchResult(TPost parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Post.SearchResult[] GetPostsSearchResult(T parsingData) + private protected virtual Search.Related.SearchResult GetRelatedSearchResult(TRelated parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Related.SearchResult GetRelatedSearchResult(T parsingData) + private protected virtual Search.Tag.SearchResult GetTagSearchResult(TTag parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Tag.SearchResult GetTagSearchResult(T parsingData) - => throw new FeatureUnavailable(); - - private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(T parsingData) + private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(TWiki parsingData) => throw new FeatureUnavailable(); + /* private protected virtual async Task GetTagEnumerableSearchResultAsync(Uri url) { if (TagsUseXml) @@ -51,7 +49,7 @@ private protected virtual async Task GetTagEnumerableSearchResultAs { return JsonConvert.DeserializeObject(await GetJsonAsync(url)); } - } + }*/ /// /// Gets whether it is possible to search for related tags on this booru. diff --git a/BooruSharp/Booru/Lolibooru.cs b/BooruSharp/Booru/Lolibooru.cs index f7b50d0..eb32a83 100644 --- a/BooruSharp/Booru/Lolibooru.cs +++ b/BooruSharp/Booru/Lolibooru.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json.Linq; - -namespace BooruSharp.Booru +namespace BooruSharp.Booru { /// /// Lolibooru. @@ -18,6 +16,8 @@ public Lolibooru() /// public override bool IsSafe => false; + /* + private protected override Search.Tag.SearchResult GetTagSearchResult(object json) { var elem = (JObject)json; @@ -27,6 +27,6 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso (Search.Tag.TagType)elem["tag_type"].Value(), elem["post_count"].Value() ); - } + }*/ } } diff --git a/BooruSharp/Booru/Parsing/EmptyParsing.cs b/BooruSharp/Booru/Parsing/EmptyParsing.cs new file mode 100644 index 0000000..d6a8375 --- /dev/null +++ b/BooruSharp/Booru/Parsing/EmptyParsing.cs @@ -0,0 +1,5 @@ +namespace BooruSharp.Booru.Parsing +{ + public class EmptyParsing + { } +} diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index e8a2b97..09f13b3 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Collections; using System.Linq; @@ -12,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Booru-on-rails https://github.com/derpibooru/booru-on-rails . This class is . /// - public abstract class BooruOnRails : ABooru + public abstract class BooruOnRails : ABooru { /// /// Initializes a new instance of the template class. @@ -48,6 +47,7 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + /* private protected override JToken ParseFirstPostSearchResult(object json) { var token = (JToken)json; @@ -137,5 +137,6 @@ private Search.Tag.TagType GetTagType(string typeName) default: return (Search.Tag.TagType)6; } } + */ } } diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 388df83..a426295 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +9,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Danbooru. This class is . /// - public abstract class Danbooru : ABooru + public abstract class Danbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -35,6 +35,7 @@ protected override void PreRequest(HttpRequestMessage message) } } + /* private protected override JToken ParseFirstPostSearchResult(object json) { JToken token = json is JArray array @@ -149,6 +150,6 @@ private protected override Search.Related.SearchResult GetRelatedSearchResult(ob elem[0].Value(), elem[1].Value() ); - } + }*/ } } diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index a482fdf..64a4a0c 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Collections.Generic; using System.Linq; @@ -10,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on E621. This class is . /// - public abstract class E621 : ABooru + public abstract class E621 : ABooru { /// /// Initializes a new instance of the template class. @@ -37,6 +37,8 @@ protected override void PreRequest(HttpRequestMessage message) } } + + /* private protected override JToken ParseFirstPostSearchResult(object json) { JObject jObject = (JObject)json; @@ -136,5 +138,6 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso } // GetRelatedSearchResult not available // TODO: Available with credentials? + */ } } diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index a0cdeee..da6cfd1 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,7 +1,6 @@ -using BooruSharp.Search; +using BooruSharp.Booru.Parsing; +using BooruSharp.Search; using BooruSharp.Search.Tag; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Globalization; @@ -16,7 +15,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Gelbooru. This class is . /// - public abstract class Gelbooru : ABooru + public abstract class Gelbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -42,6 +41,7 @@ protected override void PreRequest(HttpRequestMessage message) } } + /* /// public async override Task GetPostByMd5Async(string md5) { @@ -146,5 +146,6 @@ private protected override async Task GetTagEnumerableSearchResultA } throw new InvalidTags(); } + */ } } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 65f5a03..a91fd6f 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Globalization; using System.Linq; @@ -10,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Gelbooru 0.2. This class is . /// - public abstract class Gelbooru02 : ABooru + public abstract class Gelbooru02 : ABooru { /// /// Initializes a new instance of the template class. @@ -26,7 +26,7 @@ protected Gelbooru02(string domain, BooruOptions options = BooruOptions.None) : base(domain, UrlFormat.IndexPhp, options | BooruOptions.NoRelated | BooruOptions.NoWiki | BooruOptions.NoPostByMD5 | BooruOptions.CommentApiXml | BooruOptions.TagApiXml | BooruOptions.NoMultipleRandom) { - _url = domain; + //_url = domain; } /// @@ -38,6 +38,8 @@ protected override void PreRequest(HttpRequestMessage message) } } + /* + private protected override JToken ParseFirstPostSearchResult(object json) { JArray array = json as JArray; @@ -111,5 +113,6 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso // GetRelatedSearchResult not available private readonly string _url; + */ } } diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index afcfdd2..6d0e218 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Linq; using System.Net.Http; @@ -8,7 +8,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Moebooru. This class is . /// - public abstract class Moebooru : ABooru + public abstract class Moebooru : ABooru { /// /// Initializes a new instance of the template class. @@ -34,6 +34,7 @@ protected override void PreRequest(HttpRequestMessage message) } } + /* private protected override JToken ParseFirstPostSearchResult(object json) { JArray array = json as JArray; @@ -117,5 +118,6 @@ private protected override Search.Related.SearchResult GetRelatedSearchResult(ob elem[1].Value() ); } + */ } } diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 3c5c6fd..7801859 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,5 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Collections; using System.Linq; @@ -12,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Philomena https://github.com/ZizzyDizzyMC/philomena . This class is . /// - public abstract class Philomena : ABooru + public abstract class Philomena : ABooru { /// /// Initializes a new instance of the template class. @@ -48,6 +47,7 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + /* private protected override JToken ParseFirstPostSearchResult(object json) { var token = (JToken)json; @@ -137,5 +137,6 @@ private Search.Tag.TagType GetTagType(string typeName) default: return (Search.Tag.TagType)6; } } + */ } } diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 7c3e79c..6f8f55c 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json.Linq; +using BooruSharp.Booru.Parsing; using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +9,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Sankaku. This class is . /// - public abstract class Sankaku : ABooru + public abstract class Sankaku : ABooru { /// /// Initializes a new instance of the template class. @@ -35,6 +35,7 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't } } + /* private protected override JToken ParseFirstPostSearchResult(object json) { JArray array = json as JArray; @@ -150,5 +151,6 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso } // GetRelatedSearchResult not available + */ } } diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index 81a3fe2..b920f0b 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -29,8 +29,4 @@ - - - - diff --git a/BooruSharp/Search/Comment/ABooru.cs b/BooruSharp/Search/Comment/ABooru.cs index 812cd56..2d9628c 100644 --- a/BooruSharp/Search/Comment/ABooru.cs +++ b/BooruSharp/Search/Comment/ABooru.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -9,6 +7,7 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /* /// /// Get the comments posted on a post. /// @@ -103,5 +102,6 @@ public abstract partial class ABooru return jsonArray.Select(GetCommentSearchResult).ToArray(); } } + */ } } diff --git a/BooruSharp/Search/Favorite/ABooru.cs b/BooruSharp/Search/Favorite/ABooru.cs index 1e18fd1..3d96370 100644 --- a/BooruSharp/Search/Favorite/ABooru.cs +++ b/BooruSharp/Search/Favorite/ABooru.cs @@ -7,6 +7,7 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /* private const int _invalidAuthErrorCode = 2; /// @@ -77,5 +78,6 @@ private static async Task GetAuthResponseAndReadToEndAsync(HttpWebReques return await reader.ReadToEndAsync(); } } + */ } } diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 3efed32..6cf8f90 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Linq; using System.Threading.Tasks; using System.Xml; @@ -9,6 +7,7 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /* private const int _limitedTagsSearchCount = 2; private const int _increasedPostLimitCount = 20001; @@ -241,5 +240,6 @@ protected Search.Post.Rating GetRating(char c) default: throw new ArgumentException($"Invalid rating '{c}'.", nameof(c)); } } + */ } } diff --git a/BooruSharp/Search/Related/ABooru.cs b/BooruSharp/Search/Related/ABooru.cs index bba560a..867f239 100644 --- a/BooruSharp/Search/Related/ABooru.cs +++ b/BooruSharp/Search/Related/ABooru.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Linq; using System.Threading.Tasks; @@ -8,6 +6,7 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /* /// /// Gets the tags related to the specified . /// @@ -35,5 +34,6 @@ public abstract partial class ABooru return jsonArray.Select(GetRelatedSearchResult).ToArray(); } + */ } } diff --git a/BooruSharp/Search/Tag/ABooru.cs b/BooruSharp/Search/Tag/ABooru.cs index 0bc15be..7b4291f 100644 --- a/BooruSharp/Search/Tag/ABooru.cs +++ b/BooruSharp/Search/Tag/ABooru.cs @@ -1,6 +1,4 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -11,6 +9,7 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /* /// /// Gets information about a tag. /// @@ -140,5 +139,6 @@ private protected Search.Tag.TagType StringToTagType(string value) throw new ArgumentException($"Invalid tag '{value}'.", nameof(value)); } + */ } } diff --git a/BooruSharp/Search/Wiki/ABooru.cs b/BooruSharp/Search/Wiki/ABooru.cs index c63ad34..1d48f9e 100644 --- a/BooruSharp/Search/Wiki/ABooru.cs +++ b/BooruSharp/Search/Wiki/ABooru.cs @@ -1,12 +1,11 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; +using System; using System.Threading.Tasks; namespace BooruSharp.Booru { public abstract partial class ABooru { + /* /// /// Gets the wiki page of a tag. /// @@ -33,5 +32,6 @@ public abstract partial class ABooru throw new Search.InvalidTags(); } + */ } } From f75e1a8c162b4839e82fbc729bdcd416fb09d8c2 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 02:11:34 +0100 Subject: [PATCH 03/56] Fix errors --- BooruSharp.UnitTests/BooruSharp.UnitTests.csproj | 1 - BooruSharp.UnitTests/BooruTests.cs | 3 +-- BooruSharp/BooruSharp.csproj | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index c2e3bb4..8f85957 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -11,7 +11,6 @@ - diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 25fb418..a91694d 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,5 +1,4 @@ using BooruSharp.Booru; -using BooruSharp.Others; using System.Collections.Generic; namespace BooruSharp.UnitTests @@ -26,7 +25,7 @@ public class BooruTests new BooruTestData() { BooruType = typeof(Twibooru) }, new BooruTestData() { BooruType = typeof(Xbooru) }, new BooruTestData() { BooruType = typeof(Yandere) }, - new BooruTestData() { BooruType = typeof(Pixiv) } + //new BooruTestData() { BooruType = typeof(Pixiv) } }; } } diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index b920f0b..5d81c0f 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -8,7 +8,6 @@ BooruSharp is a C# library to browse Booru websites (Gelbooru, Konachan, etc...) easily GNU General Public License v3.0 - LICENSE https://github.com/Xwilarg/BooruSharp https://github.com/Xwilarg/BooruSharp Library From f4176c9164f0df20013de92cb2263b2ec61e13e3 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 02:12:59 +0100 Subject: [PATCH 04/56] Fix license --- BooruSharp/BooruSharp.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index 5d81c0f..7880a1a 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -14,6 +14,7 @@ Booru Gelbooru Image C-Sharp Atfbooru DanbooruDonmai Danbooru E621 E926 FurryBooru Konachan Lolibooru Realbooru Rule34 SankakuComplex Safebooru Sakugabooru Xbooru Yandere .\xmldoc\$(TargetFramework)\BooruSharp.xml + MIT From 640bd695f3f82651d52d1abbab0a8aa26e6e4462 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 02:59:18 +0100 Subject: [PATCH 05/56] Add danbooru post parsing --- .../BooruSharp.UnitTests.csproj | 9 +++ BooruSharp.UnitTests/BooruTests.cs | 80 ++++++++++++++----- BooruSharp/Booru/ABooru.cs | 5 +- BooruSharp/Booru/Template/Danbooru.cs | 63 +++++++++++---- BooruSharp/Search/Post/ABooru.cs | 30 ++++--- .../{SearchResult.cs => PostSearchResult.cs} | 19 ++--- .../{SearchResult.cs => TagSearchResult.cs} | 6 +- 7 files changed, 149 insertions(+), 63 deletions(-) rename BooruSharp/Search/Post/{SearchResult.cs => PostSearchResult.cs} (89%) rename BooruSharp/Search/Tag/{SearchResult.cs => TagSearchResult.cs} (85%) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index 8f85957..f788c3e 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -10,6 +10,15 @@ true + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index a91694d..afcc22c 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,31 +1,73 @@ using BooruSharp.Booru; +using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Xunit; namespace BooruSharp.UnitTests { public class BooruTests { - public static IEnumerable BooruParams { get; } = new[] + public static IEnumerable BooruParams { get; } = new object[][] { - new BooruTestData() { BooruType = typeof(Atfbooru) }, - new BooruTestData() { BooruType = typeof(DanbooruDonmai) }, - new BooruTestData() { BooruType = typeof(Derpibooru) }, - new BooruTestData() { BooruType = typeof(E621) }, - new BooruTestData() { BooruType = typeof(E926) }, - new BooruTestData() { BooruType = typeof(Furrybooru) }, - new BooruTestData() { BooruType = typeof(Gelbooru) }, - new BooruTestData() { BooruType = typeof(Konachan) }, - new BooruTestData() { BooruType = typeof(Lolibooru) }, - new BooruTestData() { BooruType = typeof(Ponybooru) }, - new BooruTestData() { BooruType = typeof(Realbooru) }, - new BooruTestData() { BooruType = typeof(Rule34) }, - new BooruTestData() { BooruType = typeof(Safebooru) }, - new BooruTestData() { BooruType = typeof(Sakugabooru) }, - new BooruTestData() { BooruType = typeof(SankakuComplex) }, - new BooruTestData() { BooruType = typeof(Twibooru) }, - new BooruTestData() { BooruType = typeof(Xbooru) }, - new BooruTestData() { BooruType = typeof(Yandere) }, + new object[] { new BooruTestData() { BooruType = typeof(Atfbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(DanbooruDonmai) } }, + new object[] { new BooruTestData() { BooruType = typeof(Derpibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(E621) } }, + new object[] { new BooruTestData() { BooruType = typeof(E926) } }, + new object[] { new BooruTestData() { BooruType = typeof(Furrybooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Gelbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Konachan) } }, + new object[] { new BooruTestData() { BooruType = typeof(Lolibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Ponybooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Realbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Rule34) } }, + new object[] { new BooruTestData() { BooruType = typeof(Safebooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Sakugabooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(SankakuComplex) } }, + new object[] { new BooruTestData() { BooruType = typeof(Twibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Xbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Yandere) } }, //new BooruTestData() { BooruType = typeof(Pixiv) } }; + + private static readonly Dictionary> _boorus = new Dictionary>(); + + public static Task GetAsync(Type type) + { + lock (_boorus) + { + if (!_boorus.TryGetValue(type, out var booruTask)) + { + booruTask = Task.Run(() => CreateBooruAsync(type)); + _boorus[type] = booruTask; + } + + return booruTask; + } + } + + private static async Task CreateBooruAsync(Type type) + { + var booru = (ABooru)Activator.CreateInstance(type); + + /*if (booru is Pixiv pixiv) + { + string refresh = Environment.GetEnvironmentVariable("PIXIV_REFRESH_TOKEN"); + + Skip.If(refresh == null, "Pixiv tokens aren't set."); + + await pixiv.LoginAsync(refresh); + }*/ + + return booru; + } + + [Theory] + [MemberData(nameof(BooruParams))] + public async Task SampleTest(object data) + { + var booru = await GetAsync(((BooruTestData)data).BooruType); + } } } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 0856aa8..5e2bf35 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,4 +1,5 @@ using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Collections; using System.Linq; @@ -25,13 +26,13 @@ public abstract partial class ABooru private protected virtual Search.Comment.SearchResult GetCommentSearchResult(TComment parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Post.SearchResult GetPostSearchResult(TPost parsingData) + private protected virtual PostSearchResult GetPostSearchResult(TPost parsingData) => throw new FeatureUnavailable(); private protected virtual Search.Related.SearchResult GetRelatedSearchResult(TRelated parsingData) => throw new FeatureUnavailable(); - private protected virtual Search.Tag.SearchResult GetTagSearchResult(TTag parsingData) + private protected virtual Search.Tag.TagSearchResult GetTagSearchResult(TTag parsingData) => throw new FeatureUnavailable(); private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(TWiki parsingData) diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index a426295..552d8c0 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -1,15 +1,18 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; +using BooruSharp.Search.Tag; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Reflection; namespace BooruSharp.Booru.Template { /// /// Template booru based on Danbooru. This class is . /// - public abstract class Danbooru : ABooru + public abstract class Danbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -35,24 +38,56 @@ protected override void PreRequest(HttpRequestMessage message) } } - /* - private protected override JToken ParseFirstPostSearchResult(object json) + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) { - JToken token = json is JArray array - ? array.FirstOrDefault() - : json as JToken; - - return token ?? throw new Search.InvalidTags(); + return new PostSearchResult( + fileUrl: parsingData.file_url != null ? new Uri(parsingData.file_url) : null, + previewUrl: parsingData.preview_file_url != null ? new Uri(parsingData.preview_file_url) : null, + postUrl: parsingData.id != null ? new Uri(BaseUrl + "posts/" + parsingData.id) : null, + sampleUri: parsingData.large_file_url != null ? new Uri(parsingData.large_file_url) : null, + rating: ABooru.GetRating(parsingData.rating[0]), + tags: parsingData.tag_string.Split(), + detailedTags: parsingData.tag_string_general.Split().Select(x => new TagSearchResult(-1, x, TagType.Trivia, -1)) + .Concat(parsingData.tag_string_character.Split().Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) + .Concat(parsingData.tag_string_copyright.Split().Select(x => new TagSearchResult(-1, x, TagType.Copyright, -1))) + .Concat(parsingData.tag_string_artist.Split().Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) + .Concat(parsingData.tag_string_meta.Split().Select(x => new TagSearchResult(-1, x, TagType.Metadata, -1))), + id: parsingData.id ?? 0, + size: parsingData.file_size, + height: parsingData.image_height, + width: parsingData.image_width, + previewHeight: null, + previewWidth: null, + creation: parsingData.create_at, + source: parsingData.source, + score: parsingData.score, + md5: parsingData.md5 + ); } - private protected override Search.Post.SearchResult GetPostSearchResult(JToken elem) + public class SearchResult { - var url = elem["file_url"]; - var previewUrl = elem["preview_file_url"]; - var sampleUrl = elem["large_file_url"]; - var id = elem["id"]?.Value(); - var md5 = elem["md5"]; + public string file_url; + public string preview_file_url; + public string large_file_url; + public int? id; + public string md5; + public string rating; + public string tag_string; + public string tag_string_general; + public string tag_string_character; + public string tag_string_copyright; + public string tag_string_artist; + public string tag_string_meta; + public int file_size; + public int image_height; + public int image_width; + public DateTime create_at; + public string source; + public int score; + } + /* var detailedtags = new List(); GetTags("tag_string_general", Search.Tag.TagType.Trivia); GetTags("tag_string_character", Search.Tag.TagType.Character); diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 6cf8f90..eed9f07 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -7,6 +7,20 @@ namespace BooruSharp.Booru { public abstract partial class ABooru { + /// + /// Converts a letter to its matching . + /// + public static Search.Post.Rating GetRating(char c) + { + return char.ToLower(c) switch + { + 'g' => Search.Post.Rating.General, + 's' => Search.Post.Rating.Safe, + 'q' => Search.Post.Rating.Questionable, + 'e' => Search.Post.Rating.Explicit, + _ => throw new ArgumentException($"Invalid rating '{c}'.", nameof(c)), + }; + } /* private const int _limitedTagsSearchCount = 2; private const int _increasedPostLimitCount = 20001; @@ -224,22 +238,6 @@ public virtual async Task GetPostCountAsync(params string[] tagsArg) { return GetSearchResultsFromUrlAsync(url.AbsoluteUri); } - - /// - /// Converts a letter to its matching . - /// - protected Search.Post.Rating GetRating(char c) - { - c = char.ToLower(c); - switch (c) - { - case 'g': return Search.Post.Rating.General; - case 's': return Search.Post.Rating.Safe; - case 'q': return Search.Post.Rating.Questionable; - case 'e': return Search.Post.Rating.Explicit; - default: throw new ArgumentException($"Invalid rating '{c}'.", nameof(c)); - } - } */ } } diff --git a/BooruSharp/Search/Post/SearchResult.cs b/BooruSharp/Search/Post/PostSearchResult.cs similarity index 89% rename from BooruSharp/Search/Post/SearchResult.cs rename to BooruSharp/Search/Post/PostSearchResult.cs index 3ed509b..b4dbd5c 100644 --- a/BooruSharp/Search/Post/SearchResult.cs +++ b/BooruSharp/Search/Post/PostSearchResult.cs @@ -1,4 +1,5 @@ -using System; +using BooruSharp.Search.Tag; +using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -7,7 +8,7 @@ namespace BooruSharp.Search.Post /// /// Represents a post API search result. /// - public readonly struct SearchResult + public record PostSearchResult { /// /// Initializes a struct. @@ -29,9 +30,9 @@ public readonly struct SearchResult /// The original source of the file. /// The score of the post. /// The MD5 hash of the file. - public SearchResult( - Uri fileUrl, Uri previewUrl, Uri postUrl, Uri sampleUri, Rating rating, IList tags, - IList detailedTags, int id, int? size, int height, int width, int? previewHeight, int? previewWidth, + public PostSearchResult( + Uri fileUrl, Uri previewUrl, Uri postUrl, Uri sampleUri, Rating rating, IEnumerable tags, + IEnumerable detailedTags, int id, int? size, int height, int width, int? previewHeight, int? previewWidth, DateTime? creation, string source, int? score, string md5) { FileUrl = fileUrl; @@ -39,8 +40,8 @@ public SearchResult( PostUrl = postUrl; SampleUri = sampleUri; Rating = rating; - Tags = new ReadOnlyCollection(tags); - DetailedTags = detailedTags != null ? new ReadOnlyCollection(detailedTags) : null; + Tags = tags; + DetailedTags = detailedTags; ID = id; Size = size; Height = height; @@ -81,12 +82,12 @@ public SearchResult( /// /// Gets a read-only collection containing all the tags associated with the file. /// - public ReadOnlyCollection Tags { get; } + public IEnumerable Tags { get; } /// /// Gets a read-only collection containing all the tags associated with the file with additional detail. /// - public ReadOnlyCollection DetailedTags { get; } + public IEnumerable DetailedTags { get; } /// /// Gets the ID of the post. diff --git a/BooruSharp/Search/Tag/SearchResult.cs b/BooruSharp/Search/Tag/TagSearchResult.cs similarity index 85% rename from BooruSharp/Search/Tag/SearchResult.cs rename to BooruSharp/Search/Tag/TagSearchResult.cs index 47b02eb..4cc369e 100644 --- a/BooruSharp/Search/Tag/SearchResult.cs +++ b/BooruSharp/Search/Tag/TagSearchResult.cs @@ -3,16 +3,16 @@ /// /// Represents a tag API search result. /// - public readonly struct SearchResult + public record TagSearchResult { /// - /// Initializes a struct. + /// Initializes a struct. /// /// The ID of the tag. /// The name of the tag. /// The type of the tag. /// The number of occurences of the tag. - public SearchResult(int id, string name, TagType type, int count) + public TagSearchResult(int id, string name, TagType type, int count) { ID = id; Name = name; From 667118f9c7fef5037f33588bd456e1f774eb5a70 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 10:40:41 +0100 Subject: [PATCH 06/56] Add IBooru --- BooruSharp.UnitTests/BooruTests.cs | 8 +- BooruSharp/Booru/ABooru.cs | 61 ++++---------- BooruSharp/Booru/IBooru.cs | 81 +++++++++++++++++++ BooruSharp/Booru/{ => Impl}/Atfbooru.cs | 0 BooruSharp/Booru/{ => Impl}/DanbooruDonmai.cs | 0 BooruSharp/Booru/{ => Impl}/Derpibooru.cs | 0 BooruSharp/Booru/{ => Impl}/E621.cs | 0 BooruSharp/Booru/{ => Impl}/E926.cs | 0 BooruSharp/Booru/{ => Impl}/Furrybooru.cs | 0 BooruSharp/Booru/{ => Impl}/Gelbooru.cs | 0 BooruSharp/Booru/{ => Impl}/Konachan.cs | 0 BooruSharp/Booru/{ => Impl}/Lolibooru.cs | 0 BooruSharp/Booru/{ => Impl}/Ponybooru.cs | 0 BooruSharp/Booru/{ => Impl}/Realbooru.cs | 0 BooruSharp/Booru/{ => Impl}/Rule34.cs | 0 BooruSharp/Booru/{ => Impl}/Safebooru.cs | 0 BooruSharp/Booru/{ => Impl}/Sakugabooru.cs | 0 BooruSharp/Booru/{ => Impl}/SankakuComplex.cs | 0 BooruSharp/Booru/{ => Impl}/Twibooru.cs | 0 BooruSharp/Booru/{ => Impl}/Xbooru.cs | 0 BooruSharp/Booru/{ => Impl}/Yandere.cs | 0 BooruSharp/Search/Post/ABooru.cs | 2 +- BooruSharp/Search/Post/PostSearchResult.cs | 2 +- BooruSharp/ThreadSafeRandom.cs | 4 +- 24 files changed, 104 insertions(+), 54 deletions(-) create mode 100644 BooruSharp/Booru/IBooru.cs rename BooruSharp/Booru/{ => Impl}/Atfbooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/DanbooruDonmai.cs (100%) rename BooruSharp/Booru/{ => Impl}/Derpibooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/E621.cs (100%) rename BooruSharp/Booru/{ => Impl}/E926.cs (100%) rename BooruSharp/Booru/{ => Impl}/Furrybooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Gelbooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Konachan.cs (100%) rename BooruSharp/Booru/{ => Impl}/Lolibooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Ponybooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Realbooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Rule34.cs (100%) rename BooruSharp/Booru/{ => Impl}/Safebooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Sakugabooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/SankakuComplex.cs (100%) rename BooruSharp/Booru/{ => Impl}/Twibooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Xbooru.cs (100%) rename BooruSharp/Booru/{ => Impl}/Yandere.cs (100%) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index afcc22c..a1ed8cb 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -31,9 +31,9 @@ public class BooruTests //new BooruTestData() { BooruType = typeof(Pixiv) } }; - private static readonly Dictionary> _boorus = new Dictionary>(); + private static readonly Dictionary> _boorus = new(); - public static Task GetAsync(Type type) + public static Task GetAsync(Type type) { lock (_boorus) { @@ -47,9 +47,9 @@ public static Task GetAsync(Type type) } } - private static async Task CreateBooruAsync(Type type) + private static async Task CreateBooruAsync(Type type) { - var booru = (ABooru)Activator.CreateInstance(type); + var booru = (IBooru)Activator.CreateInstance(type); /*if (booru is Pixiv pixiv) { diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 5e2bf35..14cada0 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,7 +1,6 @@ using BooruSharp.Search; using BooruSharp.Search.Post; using System; -using System.Collections; using System.Linq; using System.Net; using System.Net.Http; @@ -15,12 +14,9 @@ namespace BooruSharp.Booru /// /// Defines basic capabilities of a booru. This class is . /// - public abstract partial class ABooru + public abstract partial class ABooru : IBooru { - /// - /// Gets whether this booru is considered safe (that is, all posts on - /// this booru have rating of ). - /// + /// public abstract bool IsSafe { get; } private protected virtual Search.Comment.SearchResult GetCommentSearchResult(TComment parsingData) @@ -52,65 +48,41 @@ private protected virtual async Task GetTagEnumerableSearchResultAs } }*/ - /// - /// Gets whether it is possible to search for related tags on this booru. - /// + /// public bool HasRelatedAPI => !_options.HasFlag(BooruOptions.NoRelated); - /// - /// Gets whether it is possible to search for wiki entries on this booru. - /// + /// public bool HasWikiAPI => !_options.HasFlag(BooruOptions.NoWiki); - /// - /// Gets whether it is possible to search for comments on this booru. - /// + /// public bool HasCommentAPI => !_options.HasFlag(BooruOptions.NoComment); - /// - /// Gets whether it is possible to search for tags by their IDs on this booru. - /// + /// public bool HasTagByIdAPI => !_options.HasFlag(BooruOptions.NoTagByID); - /// - /// Gets whether it is possible to search for the last comments on this booru. - /// // As a failsafe also check for the availability of comment API. + /// public bool HasSearchLastComment => HasCommentAPI && !_options.HasFlag(BooruOptions.NoLastComments); - /// - /// Gets whether it is possible to search for posts by their MD5 on this booru. - /// + /// public bool HasPostByMd5API => !_options.HasFlag(BooruOptions.NoPostByMD5); - /// - /// Gets whether it is possible to search for posts by their ID on this booru. - /// + /// public bool HasPostByIdAPI => !_options.HasFlag(BooruOptions.NoPostByID); - /// - /// Gets whether it is possible to get the total number of posts on this booru. - /// + /// public bool HasPostCountAPI => !_options.HasFlag(BooruOptions.NoPostCount); - /// - /// Gets whether it is possible to get multiple random images on this booru. - /// + /// public bool HasMultipleRandomAPI => !_options.HasFlag(BooruOptions.NoMultipleRandom); - /// - /// Gets whether this booru supports adding or removing favorite posts. - /// + /// public bool HasFavoriteAPI => !_options.HasFlag(BooruOptions.NoFavorite); - /// - /// Gets whether this booru can't call post functions without search arguments. - /// + /// public bool NoEmptyPostSearch => _options.HasFlag(BooruOptions.NoEmptyPostSearch); - /// - /// Gets a value indicating whether searching by more than two tags at once is not allowed. - /// + /// public bool NoMoreThanTwoTags => _options.HasFlag(BooruOptions.NoMoreThan2Tags); /// @@ -133,10 +105,7 @@ private protected virtual async Task GetTagEnumerableSearchResultAs /// protected bool SearchIncreasedPostLimit => _options.HasFlag(BooruOptions.LimitOf20000); - /// - /// Checks for the booru availability. - /// Throws if service isn't available. - /// + /// public async Task CheckAvailabilityAsync() { await HttpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, _imageUrl)); diff --git a/BooruSharp/Booru/IBooru.cs b/BooruSharp/Booru/IBooru.cs new file mode 100644 index 0000000..2998563 --- /dev/null +++ b/BooruSharp/Booru/IBooru.cs @@ -0,0 +1,81 @@ +using System.Net.Http; +using System.Threading.Tasks; + +namespace BooruSharp.Booru +{ + public interface IBooru + { + /// + /// Gets whether this booru is considered safe (that is, all posts on + /// this booru have rating of ). + /// + public abstract bool IsSafe { get; } + + /// + /// Gets whether it is possible to search for related tags on this booru. + /// + public bool HasRelatedAPI { get; } + + /// + /// Gets whether it is possible to search for wiki entries on this booru. + /// + public bool HasWikiAPI { get; } + + /// + /// Gets whether it is possible to search for comments on this booru. + /// + public bool HasCommentAPI { get; } + + /// + /// Gets whether it is possible to search for tags by their IDs on this booru. + /// + public bool HasTagByIdAPI { get; } + + /// + /// Gets whether it is possible to search for the last comments on this booru. + /// + // As a failsafe also check for the availability of comment API. + public bool HasSearchLastComment { get; } + + /// + /// Gets whether it is possible to search for posts by their MD5 on this booru. + /// + public bool HasPostByMd5API { get; } + + /// + /// Gets whether it is possible to search for posts by their ID on this booru. + /// + public bool HasPostByIdAPI { get; } + + /// + /// Gets whether it is possible to get the total number of posts on this booru. + /// + public bool HasPostCountAPI { get; } + + /// + /// Gets whether it is possible to get multiple random images on this booru. + /// + public bool HasMultipleRandomAPI { get; } + + /// + /// Gets whether this booru supports adding or removing favorite posts. + /// + public bool HasFavoriteAPI { get; } + + /// + /// Gets whether this booru can't call post functions without search arguments. + /// + public bool NoEmptyPostSearch { get; } + + /// + /// Gets a value indicating whether searching by more than two tags at once is not allowed. + /// + public bool NoMoreThanTwoTags { get; } + + /// + /// Checks for the booru availability. + /// Throws if service isn't available. + /// + public Task CheckAvailabilityAsync(); + } +} diff --git a/BooruSharp/Booru/Atfbooru.cs b/BooruSharp/Booru/Impl/Atfbooru.cs similarity index 100% rename from BooruSharp/Booru/Atfbooru.cs rename to BooruSharp/Booru/Impl/Atfbooru.cs diff --git a/BooruSharp/Booru/DanbooruDonmai.cs b/BooruSharp/Booru/Impl/DanbooruDonmai.cs similarity index 100% rename from BooruSharp/Booru/DanbooruDonmai.cs rename to BooruSharp/Booru/Impl/DanbooruDonmai.cs diff --git a/BooruSharp/Booru/Derpibooru.cs b/BooruSharp/Booru/Impl/Derpibooru.cs similarity index 100% rename from BooruSharp/Booru/Derpibooru.cs rename to BooruSharp/Booru/Impl/Derpibooru.cs diff --git a/BooruSharp/Booru/E621.cs b/BooruSharp/Booru/Impl/E621.cs similarity index 100% rename from BooruSharp/Booru/E621.cs rename to BooruSharp/Booru/Impl/E621.cs diff --git a/BooruSharp/Booru/E926.cs b/BooruSharp/Booru/Impl/E926.cs similarity index 100% rename from BooruSharp/Booru/E926.cs rename to BooruSharp/Booru/Impl/E926.cs diff --git a/BooruSharp/Booru/Furrybooru.cs b/BooruSharp/Booru/Impl/Furrybooru.cs similarity index 100% rename from BooruSharp/Booru/Furrybooru.cs rename to BooruSharp/Booru/Impl/Furrybooru.cs diff --git a/BooruSharp/Booru/Gelbooru.cs b/BooruSharp/Booru/Impl/Gelbooru.cs similarity index 100% rename from BooruSharp/Booru/Gelbooru.cs rename to BooruSharp/Booru/Impl/Gelbooru.cs diff --git a/BooruSharp/Booru/Konachan.cs b/BooruSharp/Booru/Impl/Konachan.cs similarity index 100% rename from BooruSharp/Booru/Konachan.cs rename to BooruSharp/Booru/Impl/Konachan.cs diff --git a/BooruSharp/Booru/Lolibooru.cs b/BooruSharp/Booru/Impl/Lolibooru.cs similarity index 100% rename from BooruSharp/Booru/Lolibooru.cs rename to BooruSharp/Booru/Impl/Lolibooru.cs diff --git a/BooruSharp/Booru/Ponybooru.cs b/BooruSharp/Booru/Impl/Ponybooru.cs similarity index 100% rename from BooruSharp/Booru/Ponybooru.cs rename to BooruSharp/Booru/Impl/Ponybooru.cs diff --git a/BooruSharp/Booru/Realbooru.cs b/BooruSharp/Booru/Impl/Realbooru.cs similarity index 100% rename from BooruSharp/Booru/Realbooru.cs rename to BooruSharp/Booru/Impl/Realbooru.cs diff --git a/BooruSharp/Booru/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs similarity index 100% rename from BooruSharp/Booru/Rule34.cs rename to BooruSharp/Booru/Impl/Rule34.cs diff --git a/BooruSharp/Booru/Safebooru.cs b/BooruSharp/Booru/Impl/Safebooru.cs similarity index 100% rename from BooruSharp/Booru/Safebooru.cs rename to BooruSharp/Booru/Impl/Safebooru.cs diff --git a/BooruSharp/Booru/Sakugabooru.cs b/BooruSharp/Booru/Impl/Sakugabooru.cs similarity index 100% rename from BooruSharp/Booru/Sakugabooru.cs rename to BooruSharp/Booru/Impl/Sakugabooru.cs diff --git a/BooruSharp/Booru/SankakuComplex.cs b/BooruSharp/Booru/Impl/SankakuComplex.cs similarity index 100% rename from BooruSharp/Booru/SankakuComplex.cs rename to BooruSharp/Booru/Impl/SankakuComplex.cs diff --git a/BooruSharp/Booru/Twibooru.cs b/BooruSharp/Booru/Impl/Twibooru.cs similarity index 100% rename from BooruSharp/Booru/Twibooru.cs rename to BooruSharp/Booru/Impl/Twibooru.cs diff --git a/BooruSharp/Booru/Xbooru.cs b/BooruSharp/Booru/Impl/Xbooru.cs similarity index 100% rename from BooruSharp/Booru/Xbooru.cs rename to BooruSharp/Booru/Impl/Xbooru.cs diff --git a/BooruSharp/Booru/Yandere.cs b/BooruSharp/Booru/Impl/Yandere.cs similarity index 100% rename from BooruSharp/Booru/Yandere.cs rename to BooruSharp/Booru/Impl/Yandere.cs diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index eed9f07..41b2313 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -10,7 +10,7 @@ public abstract partial class ABooru /// /// Converts a letter to its matching . /// - public static Search.Post.Rating GetRating(char c) + internal static Search.Post.Rating GetRating(char c) { return char.ToLower(c) switch { diff --git a/BooruSharp/Search/Post/PostSearchResult.cs b/BooruSharp/Search/Post/PostSearchResult.cs index b4dbd5c..9ebc9b1 100644 --- a/BooruSharp/Search/Post/PostSearchResult.cs +++ b/BooruSharp/Search/Post/PostSearchResult.cs @@ -11,7 +11,7 @@ namespace BooruSharp.Search.Post public record PostSearchResult { /// - /// Initializes a struct. + /// Initializes a class. /// /// The URI of the file. /// The URI of the image thumbnail. diff --git a/BooruSharp/ThreadSafeRandom.cs b/BooruSharp/ThreadSafeRandom.cs index 1f29f93..d557abe 100644 --- a/BooruSharp/ThreadSafeRandom.cs +++ b/BooruSharp/ThreadSafeRandom.cs @@ -6,9 +6,9 @@ namespace BooruSharp { internal sealed class ThreadSafeRandom : Random, IDisposable { - private static readonly RNGCryptoServiceProvider _global = new RNGCryptoServiceProvider(); + private static readonly RNGCryptoServiceProvider _global = new(); - private readonly ThreadLocal _localRandom = new ThreadLocal(() => + private readonly ThreadLocal _localRandom = new(() => { var buffer = new byte[4]; // RNGCryptoServiceProvider is thread-safe for use in this manner From a53b80810aed9971225cd49605901223c3e66366 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 11:53:16 +0100 Subject: [PATCH 07/56] Add post parsing --- BooruSharp/Booru/ABooru.cs | 192 ++-------------------- BooruSharp/Booru/BooruOptions.cs | 82 --------- BooruSharp/Booru/IBooru.cs | 61 ------- BooruSharp/Booru/Impl/DanbooruDonmai.cs | 17 +- BooruSharp/Booru/Impl/Lolibooru.cs | 2 +- BooruSharp/Booru/Impl/Rule34.cs | 2 +- BooruSharp/Booru/Impl/Safebooru.cs | 2 +- BooruSharp/Booru/Impl/Sakugabooru.cs | 2 +- BooruSharp/Booru/Impl/Yandere.cs | 2 +- BooruSharp/Booru/Template/BooruOnRails.cs | 23 ++- BooruSharp/Booru/Template/Danbooru.cs | 30 +++- BooruSharp/Booru/Template/E621.cs | 32 +++- BooruSharp/Booru/Template/Gelbooru.cs | 24 +-- BooruSharp/Booru/Template/Gelbooru02.cs | 45 ++++- BooruSharp/Booru/Template/Moebooru.cs | 21 ++- BooruSharp/Booru/Template/Philomena.cs | 27 ++- BooruSharp/Booru/Template/Sankaku.cs | 24 ++- BooruSharp/Booru/UrlFormat.cs | 38 ----- BooruSharp/Search/Post/ABooru.cs | 88 +++------- 19 files changed, 226 insertions(+), 488 deletions(-) delete mode 100644 BooruSharp/Booru/BooruOptions.cs delete mode 100644 BooruSharp/Booru/UrlFormat.cs diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 14cada0..b825870 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,13 +1,9 @@ using BooruSharp.Search; using BooruSharp.Search.Post; using System; -using System.Linq; using System.Net; using System.Net.Http; -using System.Text.RegularExpressions; using System.Threading.Tasks; -using System.Web; -using System.Xml; namespace BooruSharp.Booru { @@ -34,77 +30,6 @@ private protected virtual Search.Tag.TagSearchResult GetTagSearchResult(TTag par private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(TWiki parsingData) => throw new FeatureUnavailable(); - /* - private protected virtual async Task GetTagEnumerableSearchResultAsync(Uri url) - { - if (TagsUseXml) - { - var xml = await GetXmlAsync(url); - return xml.LastChild; - } - else - { - return JsonConvert.DeserializeObject(await GetJsonAsync(url)); - } - }*/ - - /// - public bool HasRelatedAPI => !_options.HasFlag(BooruOptions.NoRelated); - - /// - public bool HasWikiAPI => !_options.HasFlag(BooruOptions.NoWiki); - - /// - public bool HasCommentAPI => !_options.HasFlag(BooruOptions.NoComment); - - /// - public bool HasTagByIdAPI => !_options.HasFlag(BooruOptions.NoTagByID); - - // As a failsafe also check for the availability of comment API. - /// - public bool HasSearchLastComment => HasCommentAPI && !_options.HasFlag(BooruOptions.NoLastComments); - - /// - public bool HasPostByMd5API => !_options.HasFlag(BooruOptions.NoPostByMD5); - - /// - public bool HasPostByIdAPI => !_options.HasFlag(BooruOptions.NoPostByID); - - /// - public bool HasPostCountAPI => !_options.HasFlag(BooruOptions.NoPostCount); - - /// - public bool HasMultipleRandomAPI => !_options.HasFlag(BooruOptions.NoMultipleRandom); - - /// - public bool HasFavoriteAPI => !_options.HasFlag(BooruOptions.NoFavorite); - - /// - public bool NoEmptyPostSearch => _options.HasFlag(BooruOptions.NoEmptyPostSearch); - - /// - public bool NoMoreThanTwoTags => _options.HasFlag(BooruOptions.NoMoreThan2Tags); - - /// - /// Gets a value indicating whether http:// scheme is used instead of https://. - /// - protected bool UsesHttp => _options.HasFlag(BooruOptions.UseHttp); - - /// - /// Gets a value indicating whether tags API uses XML instead of JSON. - /// - protected bool TagsUseXml => _options.HasFlag(BooruOptions.TagApiXml); - - /// - /// Gets a value indicating whether comments API uses XML instead of JSON. - /// - protected bool CommentsUseXml => _options.HasFlag(BooruOptions.CommentApiXml); - - /// - /// Gets a value indicating whether the max limit of posts per search is increased (used by Gelbooru). - /// - protected bool SearchIncreasedPostLimit => _options.HasFlag(BooruOptions.LimitOf20000); - /// public async Task CheckAvailabilityAsync() { @@ -118,6 +43,10 @@ public async Task CheckAvailabilityAsync() protected virtual void PreRequest(HttpRequestMessage message) { } + protected abstract Uri CreateQueryString(string query, string squery = "index"); + + protected abstract Task CreateRandomPostUriAsync(string[] tags); + /// /// Initializes a new instance of the class. /// @@ -129,75 +58,21 @@ protected virtual void PreRequest(HttpRequestMessage message) /// /// The options to use. Use | (bitwise OR) operator to combine multiple options. /// - protected ABooru(string domain, UrlFormat format, BooruOptions options) + protected ABooru(string domain) { Auth = null; HttpClient = null; - _options = options; - - bool useHttp = UsesHttp; // Cache returned value for faster access. - BaseUrl = new Uri("http" + (useHttp ? "" : "s") + "://" + domain, UriKind.Absolute); - _format = format; - _imageUrl = CreateQueryString(format, format == UrlFormat.Philomena ? string.Empty : "post"); - - if (_format == UrlFormat.IndexPhp) - _imageUrlXml = new Uri(_imageUrl.AbsoluteUri.Replace("json=1", "json=0")); - else if (_format == UrlFormat.PostIndexJson) - _imageUrlXml = new Uri(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")); - else - _imageUrlXml = null; - - _tagUrl = CreateQueryString(format, "tag"); - - if (HasWikiAPI) - _wikiUrl = format == UrlFormat.Danbooru - ? CreateQueryString(format, "wiki_page") - : CreateQueryString(format, "wiki"); - - if (HasRelatedAPI) - _relatedUrl = format == UrlFormat.Danbooru - ? CreateQueryString(format, "related_tag") - : CreateQueryString(format, "tag", "related"); - - if (HasCommentAPI) - _commentUrl = CreateQueryString(format, "comment"); - } - - private protected Uri CreateQueryString(UrlFormat format, string query, string squery = "index") - { - string queryString; - - switch (format) - { - case UrlFormat.PostIndexJson: - queryString = query + "/" + squery + ".json"; - break; - - case UrlFormat.IndexPhp: - queryString = "index.php?page=dapi&s=" + query + "&q=index&json=1"; - break; - case UrlFormat.Danbooru: - queryString = query == "related_tag" ? query + ".json" : query + "s.json"; - break; + BaseUrl = new Uri("https://" + domain, UriKind.Absolute); + _imageUrl = CreateQueryString("post"); - case UrlFormat.Sankaku: - queryString = query == "wiki" ? query : query + "s"; - break; + _tagUrl = CreateQueryString("tag"); - case UrlFormat.Philomena: - queryString = $"api/v1/json/search/{query}{(string.IsNullOrEmpty(query) ? string.Empty : "s")}"; - break; + _wikiUrl = CreateQueryString("wiki"); - case UrlFormat.BooruOnRails: - queryString = $"api/v3/search/{query}s"; - break; + _relatedUrl = CreateQueryString("tag", "related"); - default: - return BaseUrl; - } - - return new Uri(BaseUrl + queryString); + _commentUrl = CreateQueryString("comment"); } // TODO: Handle limitrate @@ -226,56 +101,25 @@ private protected Task GetJsonAsync(Uri url) return GetJsonAsync(url.AbsoluteUri); } - private async Task GetXmlAsync(string url) - { - var xmlDoc = new XmlDocument(); - var xmlString = await GetJsonAsync(url); - // https://www.key-shortcut.com/en/all-html-entities/all-entities/ - xmlDoc.LoadXml(Regex.Replace(xmlString, "&([a-zA-Z]+);", HttpUtility.HtmlDecode("$1"))); - return xmlDoc; - } - - private Task GetXmlAsync(Uri url) - { - return GetXmlAsync(url.AbsoluteUri); - } - - private async Task GetRandomIdAsync(string tags) - { - HttpResponseMessage msg = await HttpClient.GetAsync(BaseUrl + "index.php?page=post&s=random&" + tags); - msg.EnsureSuccessStatusCode(); - return HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id"); - } - - private Uri CreateUrl(Uri url, params string[] args) + protected Uri CreateUrl(Uri url, params string[] args) { var builder = new UriBuilder(url); if (builder.Query?.Length > 1) - builder.Query = builder.Query.Substring(1) + "&" + string.Join("&", args); + builder.Query = builder.Query[1..] + "&" + string.Join("&", args); else builder.Query = string.Join("&", args); return builder.Uri; } - private string TagsToString(string[] tags) - { - if (tags == null || !tags.Any()) - { - // Philomena doesn't support search with no tag so we search for all posts with ID > 0 - return _format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails ? "q=id.gte:0" : "tags="; - } - return (_format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails ? "q=" : "tags=") - + string.Join(_format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails ? "," : "+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(); - } - + /* private string SearchArg(string value) { return _format == UrlFormat.Danbooru ? "search[" + value + "]=" : value + "="; - } + }*/ /// /// Gets or sets authentication credentials. @@ -318,12 +162,10 @@ protected get public Uri BaseUrl { get; } private HttpClient _client; - private readonly Uri _imageUrlXml, _imageUrl, _tagUrl, _wikiUrl, _relatedUrl, _commentUrl; // URLs for differents endpoints + protected readonly Uri _imageUrl, _tagUrl, _wikiUrl, _relatedUrl, _commentUrl; // URLs for differents endpoints // All options are stored in a bit field and can be retrieved using related methods/properties. - private readonly BooruOptions _options; - private readonly UrlFormat _format; // URL format private const string _userAgentHeaderValue = "Mozilla/5.0 BooruSharp"; - private protected readonly DateTime _unixTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private protected readonly DateTime _unixTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); private static readonly Lazy _lazyClient = new Lazy(() => { var handler = new HttpClientHandler diff --git a/BooruSharp/Booru/BooruOptions.cs b/BooruSharp/Booru/BooruOptions.cs deleted file mode 100644 index 50d0cbc..0000000 --- a/BooruSharp/Booru/BooruOptions.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; - -namespace BooruSharp.Booru -{ - /// - /// Represents options for creating an object. - /// This enumeration has a attribute - /// that allows a bitwise combination of its member values. - /// - [Flags] - public enum BooruOptions - { - /// - /// Indicates that no additional options should be used when creating an object. - /// - None = 0, - /// - /// Indicates that http:// URI scheme should be used instead of https:// URI scheme. - /// - UseHttp = 1 << 0, - /// - /// Indicates that wiki API is not available. - /// - NoWiki = 1 << 1, - /// - /// Indicates that related post API is not available. - /// - NoRelated = 1 << 2, - /// - /// Indicates that comments API is not available. - /// - NoComment = 1 << 3, - /// - /// Indicates that searching for tags by ID is not available. - /// - NoTagByID = 1 << 4, - /// - /// Indicates that searching for posts by MD5 hash is not available. - /// - NoPostByMD5 = 1 << 5, - /// - /// Indicates that searching for posts by ID is not available. - /// - NoPostByID = 1 << 6, - /// - /// Indicates that latest comments API is not available. - /// - NoLastComments = 1 << 7, - /// - /// Indicates that total post count API is not available. - /// - NoPostCount = 1 << 8, - /// - /// Indicates that retrieving multiple random posts API is not available. - /// - NoMultipleRandom = 1 << 9, - /// - /// Indicates that favoriting API is not available. - /// - NoFavorite = 1 << 10, - /// - /// Indicates that comments API respond with XML instead of JSON. - /// - CommentApiXml = 1 << 11, - /// - /// Indicates that tags API respond with XML instead of JSON. - /// - TagApiXml = 1 << 12, - /// - /// Indicates that maximum limit of posts per search is increased. - /// - LimitOf20000 = 1 << 13, - /// - /// Indicates that at most 2 tags can be used for post searching. - /// - NoMoreThan2Tags = 1 << 14, - /// - /// Indicates that search parameter must be provided in order for post search functions to work. - /// - NoEmptyPostSearch = 1 << 15, - } -} diff --git a/BooruSharp/Booru/IBooru.cs b/BooruSharp/Booru/IBooru.cs index 2998563..99f18b2 100644 --- a/BooruSharp/Booru/IBooru.cs +++ b/BooruSharp/Booru/IBooru.cs @@ -11,67 +11,6 @@ public interface IBooru /// public abstract bool IsSafe { get; } - /// - /// Gets whether it is possible to search for related tags on this booru. - /// - public bool HasRelatedAPI { get; } - - /// - /// Gets whether it is possible to search for wiki entries on this booru. - /// - public bool HasWikiAPI { get; } - - /// - /// Gets whether it is possible to search for comments on this booru. - /// - public bool HasCommentAPI { get; } - - /// - /// Gets whether it is possible to search for tags by their IDs on this booru. - /// - public bool HasTagByIdAPI { get; } - - /// - /// Gets whether it is possible to search for the last comments on this booru. - /// - // As a failsafe also check for the availability of comment API. - public bool HasSearchLastComment { get; } - - /// - /// Gets whether it is possible to search for posts by their MD5 on this booru. - /// - public bool HasPostByMd5API { get; } - - /// - /// Gets whether it is possible to search for posts by their ID on this booru. - /// - public bool HasPostByIdAPI { get; } - - /// - /// Gets whether it is possible to get the total number of posts on this booru. - /// - public bool HasPostCountAPI { get; } - - /// - /// Gets whether it is possible to get multiple random images on this booru. - /// - public bool HasMultipleRandomAPI { get; } - - /// - /// Gets whether this booru supports adding or removing favorite posts. - /// - public bool HasFavoriteAPI { get; } - - /// - /// Gets whether this booru can't call post functions without search arguments. - /// - public bool NoEmptyPostSearch { get; } - - /// - /// Gets a value indicating whether searching by more than two tags at once is not allowed. - /// - public bool NoMoreThanTwoTags { get; } - /// /// Checks for the booru availability. /// Throws if service isn't available. diff --git a/BooruSharp/Booru/Impl/DanbooruDonmai.cs b/BooruSharp/Booru/Impl/DanbooruDonmai.cs index 52d3bf6..d7be217 100644 --- a/BooruSharp/Booru/Impl/DanbooruDonmai.cs +++ b/BooruSharp/Booru/Impl/DanbooruDonmai.cs @@ -1,4 +1,8 @@ -namespace BooruSharp.Booru +using System.Linq; +using System.Threading.Tasks; +using System; + +namespace BooruSharp.Booru { /// /// Danbooru. @@ -10,9 +14,18 @@ public sealed class DanbooruDonmai : Template.Danbooru /// Initializes a new instance of the class. /// public DanbooruDonmai() - : base("danbooru.donmai.us", BooruOptions.NoMoreThan2Tags) + : base("danbooru.donmai.us") { } + protected override Task CreateRandomPostUriAsync(string[] tags) + { + if (tags.Length > 2) + { + throw new Search.TooManyTags(); + } + return base.CreateRandomPostUriAsync(tags); + } + /// public override bool IsSafe => false; } diff --git a/BooruSharp/Booru/Impl/Lolibooru.cs b/BooruSharp/Booru/Impl/Lolibooru.cs index eb32a83..1844486 100644 --- a/BooruSharp/Booru/Impl/Lolibooru.cs +++ b/BooruSharp/Booru/Impl/Lolibooru.cs @@ -10,7 +10,7 @@ public sealed class Lolibooru : Template.Moebooru /// Initializes a new instance of the class. /// public Lolibooru() - : base("lolibooru.moe", BooruOptions.NoTagByID) // Tag search now returns 500 error + : base("lolibooru.moe")//TODO:, BooruOptions.NoTagByID) // Tag search now returns 500 error { } /// diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index a4507d7..2940495 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -11,7 +11,7 @@ public sealed class Rule34 : Template.Gelbooru02 /// public Rule34() // The limit is in fact 200000 but search with tags make it incredibly hard to know what is really your pid - : base("rule34.xxx", BooruOptions.NoComment | BooruOptions.LimitOf20000) + : base("rule34.xxx") //TODO:, BooruOptions.NoComment | BooruOptions.LimitOf20000) { } /// diff --git a/BooruSharp/Booru/Impl/Safebooru.cs b/BooruSharp/Booru/Impl/Safebooru.cs index 12550b3..922c427 100644 --- a/BooruSharp/Booru/Impl/Safebooru.cs +++ b/BooruSharp/Booru/Impl/Safebooru.cs @@ -10,7 +10,7 @@ public sealed class Safebooru : Template.Gelbooru02 /// Initializes a new instance of the class. /// public Safebooru() - : base("safebooru.org", BooruOptions.NoComment) + : base("safebooru.org")//TODO:, BooruOptions.NoComment) { } /// diff --git a/BooruSharp/Booru/Impl/Sakugabooru.cs b/BooruSharp/Booru/Impl/Sakugabooru.cs index b4220d9..41e9e51 100644 --- a/BooruSharp/Booru/Impl/Sakugabooru.cs +++ b/BooruSharp/Booru/Impl/Sakugabooru.cs @@ -10,7 +10,7 @@ public sealed class Sakugabooru : Template.Moebooru /// Initializes a new instance of the class. /// public Sakugabooru() - : base("sakugabooru.com", BooruOptions.NoLastComments) + : base("sakugabooru.com")//TODO:, BooruOptions.NoLastComments) { } /// diff --git a/BooruSharp/Booru/Impl/Yandere.cs b/BooruSharp/Booru/Impl/Yandere.cs index 0614450..f45f5d3 100644 --- a/BooruSharp/Booru/Impl/Yandere.cs +++ b/BooruSharp/Booru/Impl/Yandere.cs @@ -10,7 +10,7 @@ public sealed class Yandere : Template.Moebooru /// Initializes a new instance of the class. /// public Yandere() - : base("yande.re", BooruOptions.NoLastComments) + : base("yande.re")//TODO:, BooruOptions.NoLastComments) { } /// diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 09f13b3..5545dd9 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -1,6 +1,5 @@ using BooruSharp.Booru.Parsing; using System; -using System.Collections; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -20,14 +19,24 @@ public abstract class BooruOnRails : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected BooruOnRails(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.BooruOnRails, options | BooruOptions.NoFavorite | BooruOptions.NoPostByMD5 | BooruOptions.NoPostByID - | BooruOptions.NoLastComments | BooruOptions.NoWiki | BooruOptions.NoRelated) + protected BooruOnRails(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + return new($"{BaseUrl}/api/v3/search/{query}s"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + if (!tags.Any()) + { + return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=id.gte:0", "sf=random")); + } + return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "sf=random")); + } + /// /// ID used to set filter and have access to as many posts as possible /// diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 552d8c0..750ffcc 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -2,10 +2,9 @@ using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Reflection; +using System.Threading.Tasks; namespace BooruSharp.Booru.Template { @@ -21,14 +20,27 @@ public abstract class Danbooru : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Danbooru(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.Danbooru, options | BooruOptions.NoLastComments | BooruOptions.NoPostCount - | BooruOptions.NoFavorite) + protected Danbooru(string domain) : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + if (query == "tag" && squery == "related") + { + return new($"{BaseUrl}/related_tag.json"); + } + if (query == "tag" && squery == "wiki") + { + return new($"{BaseUrl}/wiki_pages.json"); + } + return new($"{BaseUrl}/{query}s.json"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "random=true")); + } + /// protected override void PreRequest(HttpRequestMessage message) { @@ -45,7 +57,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par previewUrl: parsingData.preview_file_url != null ? new Uri(parsingData.preview_file_url) : null, postUrl: parsingData.id != null ? new Uri(BaseUrl + "posts/" + parsingData.id) : null, sampleUri: parsingData.large_file_url != null ? new Uri(parsingData.large_file_url) : null, - rating: ABooru.GetRating(parsingData.rating[0]), + rating: GetRating(parsingData.rating[0]), tags: parsingData.tag_string.Split(), detailedTags: parsingData.tag_string_general.Split().Select(x => new TagSearchResult(-1, x, TagType.Trivia, -1)) .Concat(parsingData.tag_string_character.Split().Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 64a4a0c..5bc67e6 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -1,9 +1,9 @@ using BooruSharp.Booru.Parsing; using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; +using System.Threading.Tasks; namespace BooruSharp.Booru.Template { @@ -19,14 +19,32 @@ public abstract class E621 : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected E621(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.Danbooru, options | BooruOptions.NoWiki | BooruOptions.NoRelated | BooruOptions.NoComment - | BooruOptions.NoTagByID | BooruOptions.NoPostCount | BooruOptions.NoFavorite) + protected E621(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + if (query == "tag" && squery == "related") + { + return new($"{BaseUrl}/related_tag.json"); + } + if (query == "tag" && squery == "wiki") + { + return new($"{BaseUrl}/wiki_pages.json"); + } + return new($"{BaseUrl}/{query}s.json"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + if (tags.Length > 2) + { + throw new Search.TooManyTags(); + } + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "random=true")); + } + /// protected override void PreRequest(HttpRequestMessage message) { diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index da6cfd1..29e2b60 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,14 +1,8 @@ using BooruSharp.Booru.Parsing; -using BooruSharp.Search; -using BooruSharp.Search.Tag; using System; -using System.Collections; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using System.Web; -using System.Xml; namespace BooruSharp.Booru.Template { @@ -24,14 +18,20 @@ public abstract class Gelbooru : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Gelbooru(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.IndexPhp, options | BooruOptions.NoWiki | BooruOptions.NoRelated | BooruOptions.LimitOf20000 - | BooruOptions.CommentApiXml) + protected Gelbooru(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + return new($"{BaseUrl}/index.php?page=dapi&s=${query}&q=index&json=1"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+sort:random")); + } + /// protected override void PreRequest(HttpRequestMessage message) { diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index a91fd6f..6f66173 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -1,8 +1,10 @@ using BooruSharp.Booru.Parsing; using System; -using System.Globalization; using System.Linq; using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Web; using System.Xml; namespace BooruSharp.Booru.Template @@ -19,14 +21,32 @@ public abstract class Gelbooru02 : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Gelbooru02(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.IndexPhp, options | BooruOptions.NoRelated | BooruOptions.NoWiki | BooruOptions.NoPostByMD5 - | BooruOptions.CommentApiXml | BooruOptions.TagApiXml | BooruOptions.NoMultipleRandom) + protected Gelbooru02(string domain) + : base(domain) + { } + + protected override Uri CreateQueryString(string query, string squery = "index") + { + return new($"{BaseUrl}/index.php?page=dapi&s=${query}&q=index&json=1"); + } + + protected override async Task CreateRandomPostUriAsync(string[] tags) { - //_url = domain; + if (!tags.Any()) + { + // We need to request /index.php?page=post&s=random and get the id given by the redirect + HttpResponseMessage msg = await HttpClient.GetAsync($"{BaseUrl}/index.php?page=post&s=random"); + msg.EnsureSuccessStatusCode(); + return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id")); + } + var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); + XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); + int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); + + if (max == 0) + throw new Search.InvalidTags(); + + return CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); } /// @@ -38,6 +58,15 @@ protected override void PreRequest(HttpRequestMessage message) } } + private async Task GetXmlAsync(string url) + { + var xmlDoc = new XmlDocument(); + var xmlString = await GetJsonAsync(url); + // https://www.key-shortcut.com/en/all-html-entities/all-entities/ + xmlDoc.LoadXml(Regex.Replace(xmlString, "&([a-zA-Z]+);", HttpUtility.HtmlDecode("$1"))); + return xmlDoc; + } + /* private protected override JToken ParseFirstPostSearchResult(object json) diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 6d0e218..a8051e5 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -2,6 +2,9 @@ using System; using System.Linq; using System.Net.Http; +using System.Threading.Tasks; +using System.Web; +using System.Xml; namespace BooruSharp.Booru.Template { @@ -17,14 +20,20 @@ public abstract class Moebooru : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Moebooru(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.PostIndexJson, options | BooruOptions.NoPostByMD5 - | BooruOptions.NoFavorite) + protected Moebooru(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + return new($"{BaseUrl}/{query}/{squery}.json"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + } + /// protected override void PreRequest(HttpRequestMessage message) { diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 7801859..d702cdf 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,6 +1,5 @@ using BooruSharp.Booru.Parsing; using System; -using System.Collections; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -20,14 +19,28 @@ public abstract class Philomena : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Philomena(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.Philomena, options | BooruOptions.NoFavorite | BooruOptions.NoPostByMD5 | BooruOptions.NoPostByID - | BooruOptions.NoLastComments | BooruOptions.NoWiki | BooruOptions.NoRelated) + protected Philomena(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + if (query == "post") + { + return new($"{BaseUrl}/api/v1/json/search"); + } + return new($"{BaseUrl}/api/v1/json/search/{query}s"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + if (!tags.Any()) + { + return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=id.gte:0", "sf=random")); + } + return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "sf=random")); + } + /// /// ID used to set filter and have access to as many posts as possible /// diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 6f8f55c..9bbba67 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -1,8 +1,8 @@ using BooruSharp.Booru.Parsing; using System; -using System.Collections.Generic; using System.Linq; using System.Net.Http; +using System.Threading.Tasks; namespace BooruSharp.Booru.Template { @@ -18,14 +18,24 @@ public abstract class Sankaku : ABooruwww.google.com. /// - /// - /// The options to use. Use | (bitwise OR) operator to combine multiple options. - /// - protected Sankaku(string domain, BooruOptions options = BooruOptions.None) - : base(domain, UrlFormat.Sankaku, options | BooruOptions.NoRelated | BooruOptions.NoPostByMD5 | BooruOptions.NoPostByID - | BooruOptions.NoPostCount | BooruOptions.NoFavorite | BooruOptions.NoTagByID) + protected Sankaku(string domain) + : base(domain) { } + protected override Uri CreateQueryString(string query, string squery = "index") + { + if (query == "wiki") + { + return new($"{BaseUrl}/{query}"); + } + return new($"{BaseUrl}/{query}s"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + } + /// protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't work rn { diff --git a/BooruSharp/Booru/UrlFormat.cs b/BooruSharp/Booru/UrlFormat.cs deleted file mode 100644 index 6359738..0000000 --- a/BooruSharp/Booru/UrlFormat.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace BooruSharp.Booru -{ - /// - /// Indicates the API URL format that will be used to create API requests. - /// - public enum UrlFormat - { - /// - /// Indicates that the API doesn't use any particular query - /// scheme and requires custom logic to handle requests. - /// - None, - /// - /// Indicates that the API uses /post/index.json query scheme. - /// - PostIndexJson, - /// - /// Indicates that the API uses /index.php?page=dapi&s=post&q=index&json=1 query scheme. - /// - IndexPhp, - /// - /// Indicates that the API uses /posts.json query scheme. - /// - Danbooru, - /// - /// Indicates that the API uses /posts query scheme. - /// - Sankaku, - /// - /// Indicate that the API uses /api/v1/json/search/images query scheme - /// - Philomena, - /// - /// Indicate that the API uses /api/v3/search/posts query scheme - /// - BooruOnRails - } -} diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 41b2313..a60be72 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -1,26 +1,45 @@ -using System; +using BooruSharp.Search.Post; +using System; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; using System.Xml; namespace BooruSharp.Booru { - public abstract partial class ABooru + public abstract partial class ABooru : IBooru { /// /// Converts a letter to its matching . /// - internal static Search.Post.Rating GetRating(char c) + private protected static Rating GetRating(char c) { return char.ToLower(c) switch { - 'g' => Search.Post.Rating.General, - 's' => Search.Post.Rating.Safe, - 'q' => Search.Post.Rating.Questionable, - 'e' => Search.Post.Rating.Explicit, + 'g' => Rating.General, + 's' => Rating.Safe, + 'q' => Rating.Questionable, + 'e' => Rating.Explicit, _ => throw new ArgumentException($"Invalid rating '{c}'.", nameof(c)), }; } + + /// + /// Searches for a random post. If array is specified + /// and isn't empty, random post containing those tags will be returned. + /// + /// The optional array of tags that must be contained in the post. + /// The task object representing the asynchronous operation. + /// + /// + public virtual async Task GetRandomPostAsync(params string[] tagsArg) + { + string[] tags = tagsArg != null + ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() + : Array.Empty(); + + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(await CreateRandomPostUriAsync(tags)))); + } /* private const int _limitedTagsSearchCount = 2; private const int _increasedPostLimitCount = 20001; @@ -102,61 +121,6 @@ public virtual async Task GetPostCountAsync(params string[] tagsArg) } } - /// - /// Searches for a random post. If array is specified - /// and isn't empty, random post containing those tags will be returned. - /// - /// The optional array of tags that must be contained in the post. - /// The task object representing the asynchronous operation. - /// - /// - public virtual async Task GetRandomPostAsync(params string[] tagsArg) - { - string[] tags = tagsArg != null - ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() - : Array.Empty(); - - if (NoMoreThanTwoTags && tags.Length > _limitedTagsSearchCount) - throw new Search.TooManyTags(); - - string tagString = TagsToString(tags); - - if (_format == UrlFormat.IndexPhp) - { - if (this is Template.Gelbooru) - return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), tagString) + "+sort:random"); - - if (tags.Length == 0) - { - // We need to request /index.php?page=post&s=random and get the id given by the redirect - string id = await GetRandomIdAsync(tagString); - return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), "id=" + id)); - } - - // The previous option doesn't work if there are tags so we contact the XML endpoint to get post count - Uri url = CreateUrl(_imageUrlXml, GetLimit(1), tagString); - XmlDocument xml = await GetXmlAsync(url); - int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); - - if (max == 0) - throw new Search.InvalidTags(); - - if (SearchIncreasedPostLimit && max > _increasedPostLimitCount) - max = _increasedPostLimitCount; - - return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), tagString, "pid=" + Random.Next(0, max))); - } - if (_format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails) - { - return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), tagString, "sf=random")); - } - - return NoMoreThanTwoTags - // +order:random count as a tag so we use random=true instead to save one - ? await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), tagString, "random=true")) - : await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), tagString) + "+order:random"); - } - /// /// Searches for multiple random posts. If array is /// specified and isn't empty, random posts containing those tags will be returned. From 39b1c777d255e4291a78609c261275da54406f67 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 11:58:27 +0100 Subject: [PATCH 08/56] Fix Rule34 search --- BooruSharp/Booru/Impl/Rule34.cs | 30 ++++++++++++++++++++++--- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 2940495..b4ca4d8 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -1,4 +1,9 @@ -namespace BooruSharp.Booru +using System.Linq; +using System.Threading.Tasks; +using System.Xml; +using System; + +namespace BooruSharp.Booru { /// /// Rule 34. @@ -10,10 +15,29 @@ public sealed class Rule34 : Template.Gelbooru02 /// Initializes a new instance of the class. /// public Rule34() - // The limit is in fact 200000 but search with tags make it incredibly hard to know what is really your pid - : base("rule34.xxx") //TODO:, BooruOptions.NoComment | BooruOptions.LimitOf20000) + : base("rule34.xxx") //TODO:, BooruOptions.NoComment { } + protected override async Task CreateRandomPostUriAsync(string[] tags) + { + if (!tags.Any()) + { + await base.CreateRandomPostUriAsync(tags); // Nothing change here, let's just call the base class + } + var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); + XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); + int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); + + if (max == 0) + throw new Search.InvalidTags(); + + // The limit is in fact 200000 but search with tags make it incredibly hard to know what is really your pid + if (max > 20001) + max = 20001; + + return CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); + } + /// public override bool IsSafe => false; } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 6f66173..2076098 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -58,7 +58,7 @@ protected override void PreRequest(HttpRequestMessage message) } } - private async Task GetXmlAsync(string url) + protected async Task GetXmlAsync(string url) { var xmlDoc = new XmlDocument(); var xmlString = await GetJsonAsync(url); From ba8d48166d0e59a79030706aa8fe8b1f8368b2c0 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 12:07:13 +0100 Subject: [PATCH 09/56] Fix unit tests --- BooruSharp.UnitTests/BooruTests.cs | 65 +++--------------------------- BooruSharp.UnitTests/Utils.cs | 65 ++++++++++++++++++++++++++++++ BooruSharp/Booru/IBooru.cs | 13 +++++- BooruSharp/Search/Post/ABooru.cs | 14 ++----- 4 files changed, 87 insertions(+), 70 deletions(-) create mode 100644 BooruSharp.UnitTests/Utils.cs diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index a1ed8cb..6c5d802 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,6 +1,4 @@ -using BooruSharp.Booru; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -8,66 +6,15 @@ namespace BooruSharp.UnitTests { public class BooruTests { - public static IEnumerable BooruParams { get; } = new object[][] - { - new object[] { new BooruTestData() { BooruType = typeof(Atfbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(DanbooruDonmai) } }, - new object[] { new BooruTestData() { BooruType = typeof(Derpibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(E621) } }, - new object[] { new BooruTestData() { BooruType = typeof(E926) } }, - new object[] { new BooruTestData() { BooruType = typeof(Furrybooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Gelbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Konachan) } }, - new object[] { new BooruTestData() { BooruType = typeof(Lolibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Ponybooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Realbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Rule34) } }, - new object[] { new BooruTestData() { BooruType = typeof(Safebooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Sakugabooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(SankakuComplex) } }, - new object[] { new BooruTestData() { BooruType = typeof(Twibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Xbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Yandere) } }, - //new BooruTestData() { BooruType = typeof(Pixiv) } - }; - - private static readonly Dictionary> _boorus = new(); - - public static Task GetAsync(Type type) - { - lock (_boorus) - { - if (!_boorus.TryGetValue(type, out var booruTask)) - { - booruTask = Task.Run(() => CreateBooruAsync(type)); - _boorus[type] = booruTask; - } - - return booruTask; - } - } - - private static async Task CreateBooruAsync(Type type) - { - var booru = (IBooru)Activator.CreateInstance(type); - - /*if (booru is Pixiv pixiv) - { - string refresh = Environment.GetEnvironmentVariable("PIXIV_REFRESH_TOKEN"); - - Skip.If(refresh == null, "Pixiv tokens aren't set."); - - await pixiv.LoginAsync(refresh); - }*/ - - return booru; - } + // Because of compile error + public static IEnumerable BooruParams => Utils.BooruParams; [Theory] [MemberData(nameof(BooruParams))] - public async Task SampleTest(object data) + public async Task GetRandomImageAsync(object data) { - var booru = await GetAsync(((BooruTestData)data).BooruType); + var booru = await Utils.GetAsync(((BooruTestData)data).BooruType); + await booru.GetRandomPostAsync(); } } } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs new file mode 100644 index 0000000..6e802d2 --- /dev/null +++ b/BooruSharp.UnitTests/Utils.cs @@ -0,0 +1,65 @@ +using BooruSharp.Booru; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace BooruSharp.UnitTests +{ + public static class Utils + { + public static IEnumerable BooruParams { get; } = new object[][] + { + new object[] { new BooruTestData() { BooruType = typeof(Atfbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(DanbooruDonmai) } }, + new object[] { new BooruTestData() { BooruType = typeof(Derpibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(E621) } }, + new object[] { new BooruTestData() { BooruType = typeof(E926) } }, + new object[] { new BooruTestData() { BooruType = typeof(Furrybooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Gelbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Konachan) } }, + new object[] { new BooruTestData() { BooruType = typeof(Lolibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Ponybooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Realbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Rule34) } }, + new object[] { new BooruTestData() { BooruType = typeof(Safebooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Sakugabooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(SankakuComplex) } }, + new object[] { new BooruTestData() { BooruType = typeof(Twibooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Xbooru) } }, + new object[] { new BooruTestData() { BooruType = typeof(Yandere) } }, + //new BooruTestData() { BooruType = typeof(Pixiv) } + }; + + private static readonly Dictionary> _boorus = new(); + + public static Task GetAsync(Type type) + { + lock (_boorus) + { + if (!_boorus.TryGetValue(type, out var booruTask)) + { + booruTask = Task.Run(() => CreateBooruAsync(type)); + _boorus[type] = booruTask; + } + + return booruTask; + } + } + + private static async Task CreateBooruAsync(Type type) + { + var booru = (IBooru)Activator.CreateInstance(type); + + /*if (booru is Pixiv pixiv) + { + string refresh = Environment.GetEnvironmentVariable("PIXIV_REFRESH_TOKEN"); + + Skip.If(refresh == null, "Pixiv tokens aren't set."); + + await pixiv.LoginAsync(refresh); + }*/ + + return booru; + } + } +} diff --git a/BooruSharp/Booru/IBooru.cs b/BooruSharp/Booru/IBooru.cs index 99f18b2..44a827c 100644 --- a/BooruSharp/Booru/IBooru.cs +++ b/BooruSharp/Booru/IBooru.cs @@ -1,4 +1,5 @@ -using System.Net.Http; +using BooruSharp.Search.Post; +using System.Net.Http; using System.Threading.Tasks; namespace BooruSharp.Booru @@ -16,5 +17,15 @@ public interface IBooru /// Throws if service isn't available. /// public Task CheckAvailabilityAsync(); + + /// + /// Searches for a random post. If array is specified + /// and isn't empty, random post containing those tags will be returned. + /// + /// The optional array of tags that must be contained in the post. + /// The task object representing the asynchronous operation. + /// + /// + public Task GetRandomPostAsync(params string[] tagsArg); } } diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index a60be72..989522b 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -24,21 +24,15 @@ private protected static Rating GetRating(char c) }; } - /// - /// Searches for a random post. If array is specified - /// and isn't empty, random post containing those tags will be returned. - /// - /// The optional array of tags that must be contained in the post. - /// The task object representing the asynchronous operation. - /// - /// - public virtual async Task GetRandomPostAsync(params string[] tagsArg) + /// + public async Task GetRandomPostAsync(params string[] tagsArg) { string[] tags = tagsArg != null ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() : Array.Empty(); - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(await CreateRandomPostUriAsync(tags)))); + var url = await CreateRandomPostUriAsync(tags); + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url)).FirstOrDefault()); } /* private const int _limitedTagsSearchCount = 2; From f9d8161ff0baca35ef94e4dd79c74bad624ed360 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 12:26:34 +0100 Subject: [PATCH 10/56] Add BooruOnRails parsing --- BooruSharp/Booru/SnakeCaseNamingPolicy.cs | 26 ++++++++ BooruSharp/Booru/Template/BooruOnRails.cs | 54 +++++++++++++++- BooruSharp/Booru/Template/Danbooru.cs | 74 +++++++++++----------- BooruSharp/Search/Post/ABooru.cs | 6 +- BooruSharp/Search/Post/PostSearchResult.cs | 9 ++- 5 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 BooruSharp/Booru/SnakeCaseNamingPolicy.cs diff --git a/BooruSharp/Booru/SnakeCaseNamingPolicy.cs b/BooruSharp/Booru/SnakeCaseNamingPolicy.cs new file mode 100644 index 0000000..5cb8b60 --- /dev/null +++ b/BooruSharp/Booru/SnakeCaseNamingPolicy.cs @@ -0,0 +1,26 @@ +using System.Text; +using System.Text.Json; + +namespace BooruSharp.Booru +{ + internal class SnakeCaseNamingPolicy : JsonNamingPolicy + { + public override string ConvertName(string name) + { + StringBuilder str = new(); + str.Append(char.ToLower(name[0])); + foreach (char c in name[1..]) + { + if (char.IsUpper(c)) + { + str.Append("_" + char.ToLower(c)); + } + else + { + str.Append(c); + } + } + return str.ToString(); + } + } +} diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 5545dd9..8f76d9d 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -1,4 +1,5 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -10,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Booru-on-rails https://github.com/derpibooru/booru-on-rails . This class is . /// - public abstract class BooruOnRails : ABooru + public abstract class BooruOnRails : ABooru { /// /// Initializes a new instance of the template class. @@ -56,6 +57,57 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new(parsingData.Representations.Full), + previewUrl: null, + postUrl: new($"{BaseUrl}images/{parsingData.Id}"), + sampleUri: new(parsingData.Representations.Thumb), + rating: parsingData.Rating switch + { + "safe" => Rating.General, + "suggestive" => Rating.Safe, + "questionable" => Rating.Questionable, + "explicit" => Rating.Explicit, + _ => throw new($"Unknown rating {parsingData.Rating}") + }, + tags: parsingData.Tags, + detailedTags: null, + id: parsingData.Id, + size: parsingData.Size, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: parsingData.CreatedAt, + source: parsingData.SourceUrl, + score: parsingData.Score, + hash: parsingData.Sha512Hash + ); + } + + public class SearchResult + { + public Representations Representations { set; get; } + public int Id { set; get; } + public string Rating { set; get; } + public string[] Tags { set; get; } + public int Size { set; get; } + public int Width { set; get; } + public int Height { set; get; } + public DateTime CreatedAt { set; get; } + public string SourceUrl { set; get; } + public int Score { set; get; } + public string Sha512Hash { set; get; } + } + + public class Representations + { + public string Full { set; get; } + public string Thumb { set; get; } + } + /* private protected override JToken ParseFirstPostSearchResult(object json) { diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 750ffcc..dacd5bd 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -53,50 +53,50 @@ protected override void PreRequest(HttpRequestMessage message) private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) { return new PostSearchResult( - fileUrl: parsingData.file_url != null ? new Uri(parsingData.file_url) : null, - previewUrl: parsingData.preview_file_url != null ? new Uri(parsingData.preview_file_url) : null, - postUrl: parsingData.id != null ? new Uri(BaseUrl + "posts/" + parsingData.id) : null, - sampleUri: parsingData.large_file_url != null ? new Uri(parsingData.large_file_url) : null, - rating: GetRating(parsingData.rating[0]), - tags: parsingData.tag_string.Split(), - detailedTags: parsingData.tag_string_general.Split().Select(x => new TagSearchResult(-1, x, TagType.Trivia, -1)) - .Concat(parsingData.tag_string_character.Split().Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) - .Concat(parsingData.tag_string_copyright.Split().Select(x => new TagSearchResult(-1, x, TagType.Copyright, -1))) - .Concat(parsingData.tag_string_artist.Split().Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) - .Concat(parsingData.tag_string_meta.Split().Select(x => new TagSearchResult(-1, x, TagType.Metadata, -1))), - id: parsingData.id ?? 0, - size: parsingData.file_size, - height: parsingData.image_height, - width: parsingData.image_width, + fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, + previewUrl: parsingData.PreviewFileUrl != null ? new Uri(parsingData.PreviewFileUrl) : null, + postUrl: parsingData.Id != null ? new Uri(BaseUrl + "posts/" + parsingData.Id) : null, + sampleUri: parsingData.LargeFileUrl != null ? new Uri(parsingData.LargeFileUrl) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.TagString.Split(), + detailedTags: parsingData.TagStringGeneral.Split().Select(x => new TagSearchResult(-1, x, TagType.Trivia, -1)) + .Concat(parsingData.TagStringCharacter.Split().Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) + .Concat(parsingData.TagStringCopyright.Split().Select(x => new TagSearchResult(-1, x, TagType.Copyright, -1))) + .Concat(parsingData.TagStringArtist.Split().Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) + .Concat(parsingData.TagStringMeta.Split().Select(x => new TagSearchResult(-1, x, TagType.Metadata, -1))), + id: parsingData.Id ?? 0, + size: parsingData.FileSize, + height: parsingData.ImageHeight, + width: parsingData.ImageWidth, previewHeight: null, previewWidth: null, - creation: parsingData.create_at, - source: parsingData.source, - score: parsingData.score, - md5: parsingData.md5 + creation: parsingData.CreatedAt, + source: parsingData.Source, + score: parsingData.Score, + hash: parsingData.Md5 ); } public class SearchResult { - public string file_url; - public string preview_file_url; - public string large_file_url; - public int? id; - public string md5; - public string rating; - public string tag_string; - public string tag_string_general; - public string tag_string_character; - public string tag_string_copyright; - public string tag_string_artist; - public string tag_string_meta; - public int file_size; - public int image_height; - public int image_width; - public DateTime create_at; - public string source; - public int score; + public string FileUrl { set; get; } + public string PreviewFileUrl { set; get; } + public string LargeFileUrl { set; get; } + public int? Id { set; get; } + public string Md5 { set; get; } + public string Rating { set; get; } + public string TagString { set; get; } + public string TagStringGeneral { set; get; } + public string TagStringCharacter { set; get; } + public string TagStringCopyright { set; get; } + public string TagStringArtist { set; get; } + public string TagStringMeta { set; get; } + public int FileSize { set; get; } + public int ImageHeight { set; get; } + public int ImageWidth { set; get; } + public DateTime CreatedAt { set; get; } + public string Source { set; get; } + public int Score { set; get; } } /* diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 989522b..ccddfaf 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Text.Json; using System.Threading.Tasks; -using System.Xml; namespace BooruSharp.Booru { @@ -32,7 +31,10 @@ public async Task GetRandomPostAsync(params string[] tagsArg) : Array.Empty(); var url = await CreateRandomPostUriAsync(tags); - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url)).FirstOrDefault()); + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }).FirstOrDefault()); } /* private const int _limitedTagsSearchCount = 2; diff --git a/BooruSharp/Search/Post/PostSearchResult.cs b/BooruSharp/Search/Post/PostSearchResult.cs index 9ebc9b1..a047c90 100644 --- a/BooruSharp/Search/Post/PostSearchResult.cs +++ b/BooruSharp/Search/Post/PostSearchResult.cs @@ -33,7 +33,7 @@ public record PostSearchResult public PostSearchResult( Uri fileUrl, Uri previewUrl, Uri postUrl, Uri sampleUri, Rating rating, IEnumerable tags, IEnumerable detailedTags, int id, int? size, int height, int width, int? previewHeight, int? previewWidth, - DateTime? creation, string source, int? score, string md5) + DateTime? creation, string source, int? score, string hash) { FileUrl = fileUrl; PreviewUrl = previewUrl; @@ -51,7 +51,7 @@ public PostSearchResult( Creation = creation; Source = source; Score = score; - MD5 = md5; + Hash = hash; } /// @@ -140,9 +140,8 @@ public PostSearchResult( public int? Score { get; } /// - /// Gets the MD5 hash of the file, represented as - /// a sequence of 32 hexadecimal lowercase digits. + /// Gets the hash of the file /// - public string MD5 { get; } + public string Hash { get; } } } From 7ea57039f0567c2f92dcdcf429a10bccad97ae59 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 16:18:48 +0100 Subject: [PATCH 11/56] Add E621 parsing --- BooruSharp/Booru/Template/BooruOnRails.cs | 2 +- BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/E621.cs | 75 +++++++++++++++++++++- BooruSharp/Search/Post/PostSearchResult.cs | 10 +-- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 8f76d9d..f41d5e2 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -81,7 +81,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par previewHeight: null, previewWidth: null, creation: parsingData.CreatedAt, - source: parsingData.SourceUrl, + sources: string.IsNullOrEmpty(parsingData.SourceUrl) ? Array.Empty() : new[] { parsingData.SourceUrl }, score: parsingData.Score, hash: parsingData.Sha512Hash ); diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index dacd5bd..a22b9df 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -71,7 +71,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par previewHeight: null, previewWidth: null, creation: parsingData.CreatedAt, - source: parsingData.Source, + sources: string.IsNullOrEmpty(parsingData.Source) ? Array.Empty() : new[] { parsingData.Source }, score: parsingData.Score, hash: parsingData.Md5 ); diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 5bc67e6..189d379 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -1,4 +1,6 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; +using BooruSharp.Search.Tag; using System; using System.Linq; using System.Net.Http; @@ -10,7 +12,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on E621. This class is . /// - public abstract class E621 : ABooru + public abstract class E621 : ABooru { /// /// Initializes a new instance of the template class. @@ -55,6 +57,77 @@ protected override void PreRequest(HttpRequestMessage message) } } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: parsingData.Url.Url != null ? new Uri(parsingData.Url.Url) : null, + previewUrl: parsingData.Preview.Url != null ? new Uri(parsingData.Preview.Url) : null, + postUrl: new Uri($"{BaseUrl}/posts/{parsingData.Id}"), + sampleUri: parsingData.Sample.Url != null ? new Uri(parsingData.Sample.Url) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.General + .Concat(parsingData.Tags.Species) + .Concat(parsingData.Tags.Character) + .Concat(parsingData.Tags.Artist) + .Concat(parsingData.Tags.Invalid) + .Concat(parsingData.Tags.Lore) + .Concat(parsingData.Tags.Meta), + detailedTags: parsingData.Tags.Species.Select(x => new TagSearchResult(-1, x, TagType.Species, -1)) + .Concat(parsingData.Tags.Character.Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) + .Concat(parsingData.Tags.Artist.Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) + .Concat(parsingData.Tags.Invalid.Select(x => new TagSearchResult(-1, x, TagType.Invalid, -1))) + .Concat(parsingData.Tags.Lore.Select(x => new TagSearchResult(-1, x, TagType.Lore, -1))) + .Concat(parsingData.Tags.Meta.Select(x => new TagSearchResult(-1, x, TagType.Metadata, -1))), + id: parsingData.Id, + size: parsingData.Url.Size, + height: parsingData.Url.Height, + width: parsingData.Url.Width, + previewHeight: parsingData.Preview.Height, + previewWidth: parsingData.Preview.Width, + creation: parsingData.CreatedAt, + sources: parsingData.Sources ?? Array.Empty(), + score: parsingData.Score.Total, + hash: parsingData.Url.Md5 + ); + } + + public class SearchResult + { + public ImageData Url; + public ImageData Preview; + public ImageData Sample; + public Tags Tags; + public int Id; + public string Rating; + public DateTime CreatedAt; + public string[] Sources; + public Score Score; + } + + public class ImageData + { + public string Url { get; set; } + public int Width { set; get; } + public int Height { set; get; } + public int Size { set; get; } + public string Md5 { set; get; } + } + + public class Tags + { + public string[] General; + public string[] Species; + public string[] Character; + public string[] Artist; + public string[] Invalid; + public string[] Lore; + public string[] Meta; + } + + public class Score + { + public int Total; + } /* private protected override JToken ParseFirstPostSearchResult(object json) diff --git a/BooruSharp/Search/Post/PostSearchResult.cs b/BooruSharp/Search/Post/PostSearchResult.cs index a047c90..186781c 100644 --- a/BooruSharp/Search/Post/PostSearchResult.cs +++ b/BooruSharp/Search/Post/PostSearchResult.cs @@ -27,13 +27,13 @@ public record PostSearchResult /// The height of the preview image, in pixels. /// The width of the preview image, in pixels. /// The creation date of the post. - /// The original source of the file. + /// The original source of the file. /// The score of the post. - /// The MD5 hash of the file. + /// The MD5 hash of the file. public PostSearchResult( Uri fileUrl, Uri previewUrl, Uri postUrl, Uri sampleUri, Rating rating, IEnumerable tags, IEnumerable detailedTags, int id, int? size, int height, int width, int? previewHeight, int? previewWidth, - DateTime? creation, string source, int? score, string hash) + DateTime? creation, string[] sources, int? score, string hash) { FileUrl = fileUrl; PreviewUrl = previewUrl; @@ -49,7 +49,7 @@ public PostSearchResult( PreviewHeight = previewHeight; PreviewWidth = previewWidth; Creation = creation; - Source = source; + Sources = sources; Score = score; Hash = hash; } @@ -131,7 +131,7 @@ public PostSearchResult( /// /// Gets the original source of the file. /// - public string Source { get; } + public string[] Sources { get; } /// /// Gets the score of the post, or From 18a6c28db64a2e7c92bc5222761c1432a2f0cb46 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 17:52:59 +0100 Subject: [PATCH 12/56] Implement remaining boorus --- BooruSharp/Booru/Impl/Furrybooru.cs | 4 +- BooruSharp/Booru/Impl/Rule34.cs | 2 +- BooruSharp/Booru/Template/Gelbooru.cs | 45 +++++++++++- BooruSharp/Booru/Template/Gelbooru02.cs | 39 ++++++++++- BooruSharp/Booru/Template/Moebooru.cs | 47 ++++++++++++- BooruSharp/Booru/Template/Philomena.cs | 54 ++++++++++++++- BooruSharp/Booru/Template/Sankaku.cs | 92 +++++++++++++++++++++---- 7 files changed, 260 insertions(+), 23 deletions(-) diff --git a/BooruSharp/Booru/Impl/Furrybooru.cs b/BooruSharp/Booru/Impl/Furrybooru.cs index 9229ec9..8b4bd60 100644 --- a/BooruSharp/Booru/Impl/Furrybooru.cs +++ b/BooruSharp/Booru/Impl/Furrybooru.cs @@ -1,6 +1,4 @@ -using System; - -namespace BooruSharp.Booru +namespace BooruSharp.Booru { /// /// FurryBooru. diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index b4ca4d8..6640b82 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -15,7 +15,7 @@ public sealed class Rule34 : Template.Gelbooru02 /// Initializes a new instance of the class. /// public Rule34() - : base("rule34.xxx") //TODO:, BooruOptions.NoComment + : base("api.rule34.xxx") //TODO:, BooruOptions.NoComment { } protected override async Task CreateRandomPostUriAsync(string[] tags) diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 29e2b60..68ea7c2 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,15 +1,19 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; +using BooruSharp.Search.Tag; using System; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using System.Web; namespace BooruSharp.Booru.Template { /// /// Template booru based on Gelbooru. This class is . /// - public abstract class Gelbooru : ABooru + public abstract class Gelbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -41,6 +45,45 @@ protected override void PreRequest(HttpRequestMessage message) } } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new(parsingData.FileUrl), + previewUrl: new(parsingData.PreviewUrl), + postUrl: new Uri($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), + detailedTags: null, + id: parsingData.Id, + size: null, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: DateTime.ParseExact(parsingData.CreatedAt, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture), + sources: string.IsNullOrEmpty(parsingData.Source) ? Array.Empty() : new[] { parsingData.Source }, + score: parsingData.Score, + hash: parsingData.Md5 + ); + } + + public class SearchResult + { + public string FileUrl; + public string PreviewUrl; + public string SampleUrl; + public int Id; + public string Rating; + public string Tags; + public int Height; + public int Width; + public string CreatedAt; + public string Source; + public int Score; + public string Md5; + } + /* /// public async override Task GetPostByMd5Async(string md5) diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 2076098..103433b 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -1,4 +1,5 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -12,7 +13,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Gelbooru 0.2. This class is . /// - public abstract class Gelbooru02 : ABooru + public abstract class Gelbooru02 : ABooru { /// /// Initializes a new instance of the template class. @@ -58,6 +59,42 @@ protected override void PreRequest(HttpRequestMessage message) } } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new($"{BaseUrl}/images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{BaseUrl}/thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample ? new($"{BaseUrl}/samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split(), + detailedTags: null, + id: parsingData.Id, + size: null, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: null, + sources: null, + score: parsingData.Score, + hash: null + ); + } + + public class SearchResult + { + public string Directory; + public string Image; + public int Id; + public bool Sample; + public string Rating; + public string Tags; + public int Height; + public int Width; + public int Score; + } + protected async Task GetXmlAsync(string url) { var xmlDoc = new XmlDocument(); diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index a8051e5..838a797 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -1,17 +1,18 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; using System; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; using System.Web; -using System.Xml; namespace BooruSharp.Booru.Template { /// /// Template booru based on Moebooru. This class is . /// - public abstract class Moebooru : ABooru + public abstract class Moebooru : ABooru { /// /// Initializes a new instance of the template class. @@ -43,6 +44,48 @@ protected override void PreRequest(HttpRequestMessage message) } } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new(parsingData.FileUrl), + previewUrl: new(parsingData.PreviewUrl), + postUrl: new Uri($"{BaseUrl}/post/show/{parsingData.Id}"), + sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), + detailedTags: null, + id: parsingData.Id, + size: parsingData.FileSize, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: parsingData.PreviewHeight, + previewWidth: parsingData.PreviewWidth, + creation: _unixTime.AddSeconds(parsingData.CreatedAt), + sources: string.IsNullOrEmpty(parsingData.Source) ? Array.Empty() : new[] { parsingData.Source }, + score: parsingData.Score, + hash: parsingData.Md5 + ); + } + + public class SearchResult + { + public string FileUrl; + public string PreviewUrl; + public string SampleUrl; + public int Id; + public string Rating; + public string Tags; + public int FileSize; + public int Height; + public int Width; + public int PreviewHeight; + public int PreviewWidth; + public int CreatedAt; + public string Source; + public int Score; + public string Md5; + } + /* private protected override JToken ParseFirstPostSearchResult(object json) { diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index d702cdf..b684551 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,4 +1,5 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -10,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Philomena https://github.com/ZizzyDizzyMC/philomena . This class is . /// - public abstract class Philomena : ABooru + public abstract class Philomena : ABooru { /// /// Initializes a new instance of the template class. @@ -60,6 +61,57 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new(parsingData.Representations.Full), + previewUrl: null, + postUrl: new($"{BaseUrl}images/{parsingData.Id}"), + sampleUri: new(parsingData.Representations.Thumb), + rating: parsingData.Rating switch + { + "safe" => Rating.General, + "suggestive" => Rating.Safe, + "questionable" => Rating.Questionable, + "explicit" => Rating.Explicit, + _ => throw new($"Unknown rating {parsingData.Rating}") + }, + tags: parsingData.Tags, + detailedTags: null, + id: parsingData.Id, + size: parsingData.Size, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: parsingData.CreatedAt, + sources: string.IsNullOrEmpty(parsingData.SourceUrl) ? Array.Empty() : new[] { parsingData.SourceUrl }, + score: parsingData.Score, + hash: parsingData.Sha512Hash + ); + } + + public class SearchResult + { + public Representations Representations { set; get; } + public int Id { set; get; } + public string Rating { set; get; } + public string[] Tags { set; get; } + public int Size { set; get; } + public int Width { set; get; } + public int Height { set; get; } + public DateTime CreatedAt { set; get; } + public string SourceUrl { set; get; } + public int Score { set; get; } + public string Sha512Hash { set; get; } + } + + public class Representations + { + public string Full { set; get; } + public string Thumb { set; get; } + } + /* private protected override JToken ParseFirstPostSearchResult(object json) { diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 9bbba67..458bb7e 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -1,15 +1,19 @@ using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; +using BooruSharp.Search.Tag; using System; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using System.Web; namespace BooruSharp.Booru.Template { /// /// Template booru based on Sankaku. This class is . /// - public abstract class Sankaku : ABooru + public abstract class Sankaku : ABooru { /// /// Initializes a new instance of the template class. @@ -45,6 +49,79 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't } } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, + previewUrl: parsingData.PreviewUrl != null ? new(parsingData.PreviewUrl) : null, + postUrl: new UriBuilder(BaseUrl) + { + Host = BaseUrl.Host.Replace("capi-v2", "beta"), + Path = $"/post/show/{parsingData.Id}", + }.Uri, + sampleUri: parsingData.SampleUrl != null && parsingData.SampleUrl.Contains("/preview/") ? new Uri(parsingData.SampleUrl) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Select(x => x.NameEn), + detailedTags: parsingData.Tags.Select(x => new TagSearchResult(x.Id, x.NameEn, GetTagType(x.Type), x.PostCount)), + id: parsingData.Id, + size: parsingData.FileSize, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: parsingData.PreviewHeight, + previewWidth: parsingData.PreviewWidth, + creation: _unixTime.AddSeconds(parsingData.CreatedAt.S), + sources: string.IsNullOrEmpty(parsingData.Source) ? Array.Empty() : new[] { parsingData.Source }, + score: parsingData.TotalScore, + hash: parsingData.Md5 + ); + } + + public class SearchResult + { + public string FileUrl; + public string PreviewUrl; + public string SampleUrl; + public int Id; + public string Rating; + public Tag[] Tags; + public int FileSize; + public int Height; + public int Width; + public int PreviewHeight; + public int PreviewWidth; + public CreationInfo CreatedAt; + public string Source; + public int TotalScore; + public string Md5; + } + + public class Tag + { + public int Id; + public string NameEn; + public int Type; + public int PostCount; + } + + public class CreationInfo + { + public int S; + } + + + private static TagType GetTagType(int type) + { + return type switch + { + 0 => TagType.Trivia, + 1 => TagType.Artist, + 3 => TagType.Copyright, + 4 => TagType.Character, + 8 => TagType.Metadata, + _ => (TagType)6, + }; + } + /* private protected override JToken ParseFirstPostSearchResult(object json) { @@ -102,19 +179,6 @@ private protected override Search.Post.SearchResult GetPostSearchResult(JToken e ); } - private Search.Tag.TagType GetTagType(int type) - { - switch(type) - { - case 0: return Search.Tag.TagType.Trivia; - case 1: return Search.Tag.TagType.Artist; - case 3: return Search.Tag.TagType.Copyright; - case 4: return Search.Tag.TagType.Character; - case 8: return Search.Tag.TagType.Metadata; - default: return (Search.Tag.TagType)6; - } - } - private protected override Search.Post.SearchResult[] GetPostsSearchResult(object json) { return json is JArray array From 58c583b9b89bdec6ea8808b71a8f197e67814c62 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 18:20:59 +0100 Subject: [PATCH 13/56] Fix JSON deserialization --- BooruSharp/Booru/ABooru.cs | 10 +++--- BooruSharp/Booru/Template/BooruOnRails.cs | 26 +++++++------- BooruSharp/Booru/Template/Danbooru.cs | 36 +++++++++---------- BooruSharp/Booru/Template/E621.cs | 44 +++++++++++------------ BooruSharp/Booru/Template/Gelbooru.cs | 25 +++++++------ BooruSharp/Booru/Template/Gelbooru02.cs | 18 +++++----- BooruSharp/Booru/Template/Moebooru.cs | 30 ++++++++-------- BooruSharp/Booru/Template/Philomena.cs | 26 +++++++------- BooruSharp/Booru/Template/Sankaku.cs | 42 +++++++++++----------- 9 files changed, 127 insertions(+), 130 deletions(-) diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index b825870..4036d97 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -77,6 +77,11 @@ protected ABooru(string domain) // TODO: Handle limitrate + private protected Task GetJsonAsync(Uri url) + { + return GetJsonAsync(url.AbsoluteUri); + } + private protected async Task GetJsonAsync(string url) { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; @@ -96,11 +101,6 @@ private protected async Task GetJsonAsync(string url) return await msg.Content.ReadAsStringAsync(); } - private protected Task GetJsonAsync(Uri url) - { - return GetJsonAsync(url.AbsoluteUri); - } - protected Uri CreateUrl(Uri url, params string[] args) { var builder = new UriBuilder(url); diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index f41d5e2..a9b5c87 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -89,23 +89,23 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public Representations Representations { set; get; } - public int Id { set; get; } - public string Rating { set; get; } - public string[] Tags { set; get; } - public int Size { set; get; } - public int Width { set; get; } - public int Height { set; get; } - public DateTime CreatedAt { set; get; } - public string SourceUrl { set; get; } - public int Score { set; get; } - public string Sha512Hash { set; get; } + public Representations Representations { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public string[] Tags { init; get; } + public int Size { init; get; } + public int Width { init; get; } + public int Height { init; get; } + public DateTime CreatedAt { init; get; } + public string SourceUrl { init; get; } + public int Score { init; get; } + public string Sha512Hash { init; get; } } public class Representations { - public string Full { set; get; } - public string Thumb { set; get; } + public string Full { init; get; } + public string Thumb { init; get; } } /* diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index a22b9df..673046e 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -79,24 +79,24 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public string FileUrl { set; get; } - public string PreviewFileUrl { set; get; } - public string LargeFileUrl { set; get; } - public int? Id { set; get; } - public string Md5 { set; get; } - public string Rating { set; get; } - public string TagString { set; get; } - public string TagStringGeneral { set; get; } - public string TagStringCharacter { set; get; } - public string TagStringCopyright { set; get; } - public string TagStringArtist { set; get; } - public string TagStringMeta { set; get; } - public int FileSize { set; get; } - public int ImageHeight { set; get; } - public int ImageWidth { set; get; } - public DateTime CreatedAt { set; get; } - public string Source { set; get; } - public int Score { set; get; } + public string FileUrl { init; get; } + public string PreviewFileUrl { init; get; } + public string LargeFileUrl { init; get; } + public int? Id { init; get; } + public string Md5 { init; get; } + public string Rating { init; get; } + public string TagString { init; get; } + public string TagStringGeneral { init; get; } + public string TagStringCharacter { init; get; } + public string TagStringCopyright { init; get; } + public string TagStringArtist { init; get; } + public string TagStringMeta { init; get; } + public int FileSize { init; get; } + public int ImageHeight { init; get; } + public int ImageWidth { init; get; } + public DateTime CreatedAt { init; get; } + public string Source { init; get; } + public int Score { init; get; } } /* diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 189d379..488925d 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -93,40 +93,40 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public ImageData Url; - public ImageData Preview; - public ImageData Sample; - public Tags Tags; - public int Id; - public string Rating; - public DateTime CreatedAt; - public string[] Sources; - public Score Score; + public ImageData Url { init; get; } + public ImageData Preview { init; get; } + public ImageData Sample { init; get; } + public Tags Tags { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public DateTime CreatedAt { init; get; } + public string[] Sources { init; get; } + public Score Score { init; get; } } public class ImageData { - public string Url { get; set; } - public int Width { set; get; } - public int Height { set; get; } - public int Size { set; get; } - public string Md5 { set; get; } + public string Url { init; get; } + public int Width { init; get; } + public int Height { init; get; } + public int Size { init; get; } + public string Md5 { init; get; } } public class Tags { - public string[] General; - public string[] Species; - public string[] Character; - public string[] Artist; - public string[] Invalid; - public string[] Lore; - public string[] Meta; + public string[] General { init; get; } + public string[] Species { init; get; } + public string[] Character { init; get; } + public string[] Artist { init; get; } + public string[] Invalid { init; get; } + public string[] Lore { init; get; } + public string[] Meta { init; get; } } public class Score { - public int Total; + public int Total { init; get; } } /* diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 68ea7c2..b94df0b 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,6 +1,5 @@ using BooruSharp.Booru.Parsing; using BooruSharp.Search.Post; -using BooruSharp.Search.Tag; using System; using System.Globalization; using System.Linq; @@ -70,18 +69,18 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public string FileUrl; - public string PreviewUrl; - public string SampleUrl; - public int Id; - public string Rating; - public string Tags; - public int Height; - public int Width; - public string CreatedAt; - public string Source; - public int Score; - public string Md5; + public string FileUrl { init; get; } + public string PreviewUrl { init; get; } + public string SampleUrl { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public string CreatedAt { init; get; } + public string Source { init; get; } + public int Score { init; get; } + public string Md5 { init; get; } } /* diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 103433b..c6f81b3 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -84,15 +84,15 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public string Directory; - public string Image; - public int Id; - public bool Sample; - public string Rating; - public string Tags; - public int Height; - public int Width; - public int Score; + public string Directory { init; get; } + public string Image { init; get; } + public int Id { init; get; } + public bool Sample { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int Score { init; get; } } protected async Task GetXmlAsync(string url) diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 838a797..eda3c7d 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -69,21 +69,21 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public string FileUrl; - public string PreviewUrl; - public string SampleUrl; - public int Id; - public string Rating; - public string Tags; - public int FileSize; - public int Height; - public int Width; - public int PreviewHeight; - public int PreviewWidth; - public int CreatedAt; - public string Source; - public int Score; - public string Md5; + public string FileUrl { init; get; } + public string PreviewUrl { init; get; } + public string SampleUrl { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int FileSize { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int PreviewHeight { init; get; } + public int PreviewWidth { init; get; } + public int CreatedAt { init; get; } + public string Source { init; get; } + public int Score { init; get; } + public string Md5 { init; get; } } /* diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index b684551..d3df901 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -93,23 +93,23 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public Representations Representations { set; get; } - public int Id { set; get; } - public string Rating { set; get; } - public string[] Tags { set; get; } - public int Size { set; get; } - public int Width { set; get; } - public int Height { set; get; } - public DateTime CreatedAt { set; get; } - public string SourceUrl { set; get; } - public int Score { set; get; } - public string Sha512Hash { set; get; } + public Representations Representations { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public string[] Tags { init; get; } + public int Size { init; get; } + public int Width { init; get; } + public int Height { init; get; } + public DateTime CreatedAt { init; get; } + public string SourceUrl { init; get; } + public int Score { init; get; } + public string Sha512Hash { init; get; } } public class Representations { - public string Full { set; get; } - public string Thumb { set; get; } + public string Full { init; get; } + public string Thumb { init; get; } } /* diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 458bb7e..9e1e2d6 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -2,7 +2,6 @@ using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -78,37 +77,36 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par public class SearchResult { - public string FileUrl; - public string PreviewUrl; - public string SampleUrl; - public int Id; - public string Rating; - public Tag[] Tags; - public int FileSize; - public int Height; - public int Width; - public int PreviewHeight; - public int PreviewWidth; - public CreationInfo CreatedAt; - public string Source; - public int TotalScore; - public string Md5; + public string FileUrl { init; get; } + public string PreviewUrl { init; get; } + public string SampleUrl { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public Tag[] Tags { init; get; } + public int FileSize { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int PreviewHeight { init; get; } + public int PreviewWidth { init; get; } + public CreationInfo CreatedAt { init; get; } + public string Source { init; get; } + public int TotalScore { init; get; } + public string Md5 { init; get; } } public class Tag { - public int Id; - public string NameEn; - public int Type; - public int PostCount; + public int Id { init; get; } + public string NameEn { init; get; } + public int Type { init; get; } + public int PostCount { init; get; } } public class CreationInfo { - public int S; + public int S { init; get; } } - private static TagType GetTagType(int type) { return type switch From 9f28af3169fdc5813c0aa1d6d060c1632a075541 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 18:28:22 +0100 Subject: [PATCH 14/56] Fix search not working for Gelboory* --- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index b94df0b..48596fb 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -27,7 +27,7 @@ protected Gelbooru(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/index.php?page=dapi&s=${query}&q=index&json=1"); + return new($"{BaseUrl}/index.php?page=dapi&s={query}&q=index&json=1"); } protected override Task CreateRandomPostUriAsync(string[] tags) diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index c6f81b3..38d56b8 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -28,7 +28,7 @@ protected Gelbooru02(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/index.php?page=dapi&s=${query}&q=index&json=1"); + return new($"{BaseUrl}/index.php?page=dapi&s={query}&q=index&json=1"); } protected override async Task CreateRandomPostUriAsync(string[] tags) From 2cc1103c3a46c615f99eb5678877f26ba4f1fdf3 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 20:35:44 +0100 Subject: [PATCH 15/56] Fix more post issues --- BooruSharp.UnitTests/BooruTests.cs | 4 +-- BooruSharp/Booru/ABooru.cs | 19 ++++++++++++ BooruSharp/Booru/Template/BooruOnRails.cs | 32 ++++++++++++++------- BooruSharp/Booru/Template/Danbooru.cs | 8 +++--- BooruSharp/Booru/Template/E621.cs | 8 +++--- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 10 +++---- BooruSharp/Booru/Template/Moebooru.cs | 5 ++-- BooruSharp/Booru/Template/Philomena.cs | 35 ++++++++++++++++------- BooruSharp/Booru/Template/Sankaku.cs | 4 +-- BooruSharp/Search/Post/ABooru.cs | 5 +--- 11 files changed, 86 insertions(+), 46 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 6c5d802..7e40074 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -11,9 +11,9 @@ public class BooruTests [Theory] [MemberData(nameof(BooruParams))] - public async Task GetRandomImageAsync(object data) + public async Task GetRandomImageAsync(BooruTestData data) { - var booru = await Utils.GetAsync(((BooruTestData)data).BooruType); + var booru = await Utils.GetAsync(data.BooruType); await booru.GetRandomPostAsync(); } } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 4036d97..1316895 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,8 +1,10 @@ using BooruSharp.Search; using BooruSharp.Search.Post; using System; +using System.Linq; using System.Net; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; namespace BooruSharp.Booru @@ -43,10 +45,27 @@ public async Task CheckAvailabilityAsync() protected virtual void PreRequest(HttpRequestMessage message) { } + /// + /// Create an URL to request the specified API + /// + /// Main query + /// Sub query protected abstract Uri CreateQueryString(string query, string squery = "index"); + /// + /// Create the Uri to request a post from the API + /// + /// List of tags sent by the user protected abstract Task CreateRandomPostUriAsync(string[] tags); + protected virtual async Task GetPostFromUriAsync(Uri url) + { + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }).FirstOrDefault()); + } + /// /// Initializes a new instance of the class. /// diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index a9b5c87..cf4a9d2 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; using System.Web; @@ -26,7 +27,7 @@ protected BooruOnRails(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/api/v3/search/{query}s"); + return new($"{BaseUrl}api/v3/search/{query}s"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -57,21 +58,28 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + protected override async Task GetPostFromUriAsync(Uri url) + { + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }).Posts.FirstOrDefault()); + } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) { + Rating rating; + if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; + else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; + else if (parsingData.Tags.Contains("suggestive")) rating = Rating.Safe; + else if (parsingData.Tags.Contains("safe")) rating = Rating.General; + else rating = (Rating)(-1); // Some images doesn't have a rating return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), previewUrl: null, postUrl: new($"{BaseUrl}images/{parsingData.Id}"), sampleUri: new(parsingData.Representations.Thumb), - rating: parsingData.Rating switch - { - "safe" => Rating.General, - "suggestive" => Rating.Safe, - "questionable" => Rating.Questionable, - "explicit" => Rating.Explicit, - _ => throw new($"Unknown rating {parsingData.Rating}") - }, + rating: rating, tags: parsingData.Tags, detailedTags: null, id: parsingData.Id, @@ -87,11 +95,15 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par ); } + public class PostContainer + { + public SearchResult[] Posts { init; get; } + } + public class SearchResult { public Representations Representations { init; get; } public int Id { init; get; } - public string Rating { init; get; } public string[] Tags { init; get; } public int Size { init; get; } public int Width { init; get; } diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 673046e..2555067 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -27,13 +27,13 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "tag" && squery == "related") { - return new($"{BaseUrl}/related_tag.json"); + return new($"{BaseUrl}related_tag.json"); } if (query == "tag" && squery == "wiki") { - return new($"{BaseUrl}/wiki_pages.json"); + return new($"{BaseUrl}wiki_pages.json"); } - return new($"{BaseUrl}/{query}s.json"); + return new($"{BaseUrl}{query}s.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -55,7 +55,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, previewUrl: parsingData.PreviewFileUrl != null ? new Uri(parsingData.PreviewFileUrl) : null, - postUrl: parsingData.Id != null ? new Uri(BaseUrl + "posts/" + parsingData.Id) : null, + postUrl: parsingData.Id != null ? new Uri($"{BaseUrl}posts/{parsingData.Id}") : null, sampleUri: parsingData.LargeFileUrl != null ? new Uri(parsingData.LargeFileUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.TagString.Split(), diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 488925d..65eb1af 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -29,13 +29,13 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "tag" && squery == "related") { - return new($"{BaseUrl}/related_tag.json"); + return new($"{BaseUrl}related_tag.json"); } if (query == "tag" && squery == "wiki") { - return new($"{BaseUrl}/wiki_pages.json"); + return new($"{BaseUrl}wiki_pages.json"); } - return new($"{BaseUrl}/{query}s.json"); + return new($"{BaseUrl}{query}s.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -62,7 +62,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par return new PostSearchResult( fileUrl: parsingData.Url.Url != null ? new Uri(parsingData.Url.Url) : null, previewUrl: parsingData.Preview.Url != null ? new Uri(parsingData.Preview.Url) : null, - postUrl: new Uri($"{BaseUrl}/posts/{parsingData.Id}"), + postUrl: new Uri($"{BaseUrl}posts/{parsingData.Id}"), sampleUri: parsingData.Sample.Url != null ? new Uri(parsingData.Sample.Url) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.General diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 48596fb..66c65ed 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -27,7 +27,7 @@ protected Gelbooru(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/index.php?page=dapi&s={query}&q=index&json=1"); + return new($"{BaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); } protected override Task CreateRandomPostUriAsync(string[] tags) diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 38d56b8..360f105 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -28,7 +28,7 @@ protected Gelbooru02(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/index.php?page=dapi&s={query}&q=index&json=1"); + return new($"{BaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); } protected override async Task CreateRandomPostUriAsync(string[] tags) @@ -36,7 +36,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) if (!tags.Any()) { // We need to request /index.php?page=post&s=random and get the id given by the redirect - HttpResponseMessage msg = await HttpClient.GetAsync($"{BaseUrl}/index.php?page=post&s=random"); + HttpResponseMessage msg = await HttpClient.GetAsync($"{BaseUrl}index.php?page=post&s=random"); msg.EnsureSuccessStatusCode(); return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id")); } @@ -62,10 +62,10 @@ protected override void PreRequest(HttpRequestMessage message) private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) { return new PostSearchResult( - fileUrl: new($"{BaseUrl}/images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{BaseUrl}/thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample ? new($"{BaseUrl}/samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + sampleUri: parsingData.Sample ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index eda3c7d..da11695 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -1,7 +1,6 @@ using BooruSharp.Booru.Parsing; using BooruSharp.Search.Post; using System; -using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -27,7 +26,7 @@ protected Moebooru(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}/{query}/{squery}.json"); + return new($"{BaseUrl}{query}/{squery}.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -49,7 +48,7 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), - postUrl: new Uri($"{BaseUrl}/post/show/{parsingData.Id}"), + postUrl: new Uri($"{BaseUrl}post/show/{parsingData.Id}"), sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index d3df901..c8366f9 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -3,8 +3,10 @@ using System; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; using System.Web; +using static BooruSharp.Booru.Template.E621; namespace BooruSharp.Booru.Template { @@ -28,9 +30,9 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "post") { - return new($"{BaseUrl}/api/v1/json/search"); + return new($"{BaseUrl}api/v1/json/search"); } - return new($"{BaseUrl}/api/v1/json/search/{query}s"); + return new($"{BaseUrl}api/v1/json/search/{query}s"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -61,21 +63,28 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + protected override async Task GetPostFromUriAsync(Uri url) + { + return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }).Images.FirstOrDefault()); + } + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) { + Rating rating; + if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; + else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; + else if (parsingData.Tags.Contains("suggestive")) rating = Rating.Safe; + else if (parsingData.Tags.Contains("safe")) rating = Rating.General; + else rating = (Rating)(-1); // Some images doesn't have a rating return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), previewUrl: null, postUrl: new($"{BaseUrl}images/{parsingData.Id}"), sampleUri: new(parsingData.Representations.Thumb), - rating: parsingData.Rating switch - { - "safe" => Rating.General, - "suggestive" => Rating.Safe, - "questionable" => Rating.Questionable, - "explicit" => Rating.Explicit, - _ => throw new($"Unknown rating {parsingData.Rating}") - }, + rating: rating, tags: parsingData.Tags, detailedTags: null, id: parsingData.Id, @@ -91,11 +100,15 @@ private protected override PostSearchResult GetPostSearchResult(SearchResult par ); } + public class PostContainer + { + public SearchResult[] Images { init; get; } + } + public class SearchResult { public Representations Representations { init; get; } public int Id { init; get; } - public string Rating { init; get; } public string[] Tags { init; get; } public int Size { init; get; } public int Width { init; get; } diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 9e1e2d6..2ace36f 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -29,9 +29,9 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "wiki") { - return new($"{BaseUrl}/{query}"); + return new($"{BaseUrl}{query}"); } - return new($"{BaseUrl}/{query}s"); + return new($"{BaseUrl}{query}s"); } protected override Task CreateRandomPostUriAsync(string[] tags) diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index ccddfaf..0e6cb6e 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -31,10 +31,7 @@ public async Task GetRandomPostAsync(params string[] tagsArg) : Array.Empty(); var url = await CreateRandomPostUriAsync(tags); - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions - { - PropertyNamingPolicy = new SnakeCaseNamingPolicy() - }).FirstOrDefault()); + return await GetPostFromUriAsync(url); } /* private const int _limitedTagsSearchCount = 2; From b474c3916072dcda1ccd6a44c346a1b29083ce12 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 20:42:34 +0100 Subject: [PATCH 16/56] Fix Safebooru crash --- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 360f105..dde6901 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -92,7 +92,7 @@ public class SearchResult public string Tags { init; get; } public int Height { init; get; } public int Width { init; get; } - public int Score { init; get; } + public int? Score { init; get; } } protected async Task GetXmlAsync(string url) From 63615a9ac16fdeee77f2ce432801c67ef532e1cc Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 7 Dec 2022 22:12:19 +0100 Subject: [PATCH 17/56] Fix crash --- BooruSharp.UnitTests/BooruTests.cs | 12 +- BooruSharp/Booru/ABooru.cs | 2 + BooruSharp/Booru/IBooru.cs | 2 + BooruSharp/Booru/Impl/Rule34.cs | 7 +- BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/MyImouto.cs | 174 +++++++++++++++++++++++++ BooruSharp/Booru/Template/Philomena.cs | 1 - README.md | 41 +++--- 8 files changed, 215 insertions(+), 26 deletions(-) create mode 100644 BooruSharp/Booru/Template/MyImouto.cs diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 7e40074..cb90fd0 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using BooruSharp.Search; +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; @@ -14,7 +15,14 @@ public class BooruTests public async Task GetRandomImageAsync(BooruTestData data) { var booru = await Utils.GetAsync(data.BooruType); - await booru.GetRandomPostAsync(); + if (booru.CanSearchWithNoTag) + { + await booru.GetRandomPostAsync(); + } + else + { + await Assert.ThrowsAsync(async () => { await booru.GetRandomPostAsync(); }); + } } } } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 1316895..86a5d49 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -14,6 +14,8 @@ namespace BooruSharp.Booru /// public abstract partial class ABooru : IBooru { + public virtual bool CanSearchWithNoTag => true; + /// public abstract bool IsSafe { get; } diff --git a/BooruSharp/Booru/IBooru.cs b/BooruSharp/Booru/IBooru.cs index 44a827c..3c219ee 100644 --- a/BooruSharp/Booru/IBooru.cs +++ b/BooruSharp/Booru/IBooru.cs @@ -6,6 +6,8 @@ namespace BooruSharp.Booru { public interface IBooru { + public bool CanSearchWithNoTag { get; } + /// /// Gets whether this booru is considered safe (that is, all posts on /// this booru have rating of ). diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 6640b82..5cfd739 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using System.Xml; using System; +using BooruSharp.Search; namespace BooruSharp.Booru { @@ -18,18 +19,20 @@ public Rule34() : base("api.rule34.xxx") //TODO:, BooruOptions.NoComment { } + public override bool CanSearchWithNoTag => false; + protected override async Task CreateRandomPostUriAsync(string[] tags) { if (!tags.Any()) { - await base.CreateRandomPostUriAsync(tags); // Nothing change here, let's just call the base class + throw new FeatureUnavailable("Rule34 doesn't support searchs with no tag"); } var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) - throw new Search.InvalidTags(); + throw new InvalidTags(); // The limit is in fact 200000 but search with tags make it incredibly hard to know what is really your pid if (max > 20001) diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 2555067..16c177e 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -9,7 +9,7 @@ namespace BooruSharp.Booru.Template { /// - /// Template booru based on Danbooru. This class is . + /// Template booru based on Danbooru https://github.com/danbooru/danbooru . This class is . /// public abstract class Danbooru : ABooru { diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs new file mode 100644 index 0000000..8bbc4b3 --- /dev/null +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -0,0 +1,174 @@ +using BooruSharp.Booru.Parsing; +using BooruSharp.Search.Post; +using System; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web; + +namespace BooruSharp.Booru.Template +{ + /// + /// Template booru based on MyImouto. This class is . + /// + public abstract class MyImouto : ABooru + { + /// + /// Initializes a new instance of the template class. + /// + /// + /// The fully qualified domain name. Example domain + /// name should look like www.google.com. + /// + protected MyImouto(string domain) + : base(domain) + { } + + protected override Uri CreateQueryString(string query, string squery = "index") + { + return new($"{BaseUrl}{query}/{squery}.json"); + } + + protected override Task CreateRandomPostUriAsync(string[] tags) + { + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + } + + /// + protected override void PreRequest(HttpRequestMessage message) + { + if (Auth != null) + { + message.Headers.Add("Cookie", "user_id=" + Auth.UserId + ";pass_hash=" + Auth.PasswordHash); + } + } + + private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + { + return new PostSearchResult( + fileUrl: new(parsingData.FileUrl), + previewUrl: new(parsingData.PreviewUrl), + postUrl: new Uri($"{BaseUrl}post/show/{parsingData.Id}"), + sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), + detailedTags: null, + id: parsingData.Id, + size: parsingData.FileSize, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: parsingData.PreviewHeight, + previewWidth: parsingData.PreviewWidth, + creation: _unixTime.AddSeconds(parsingData.CreatedAt), + sources: string.IsNullOrEmpty(parsingData.Source) ? Array.Empty() : new[] { parsingData.Source }, + score: int.Parse(parsingData.Score), + hash: parsingData.Md5 + ); + } + + public class SearchResult + { + public string FileUrl { init; get; } + public string PreviewUrl { init; get; } + public string SampleUrl { init; get; } + public int Id { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int FileSize { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int PreviewHeight { init; get; } + public int PreviewWidth { init; get; } + public int CreatedAt { init; get; } + public string Source { init; get; } + public string Score { init; get; } + public string Md5 { init; get; } + } + + /* + private protected override JToken ParseFirstPostSearchResult(object json) + { + JArray array = json as JArray; + return array?.FirstOrDefault() ?? throw new Search.InvalidTags(); + } + + private protected override Search.Post.SearchResult GetPostSearchResult(JToken elem) + { + int id = elem["id"].Value(); + var sampleUrl = elem["sample_url"].Value(); + + return new Search.Post.SearchResult( + new Uri(elem["file_url"].Value()), + new Uri(elem["preview_url"].Value()), + new Uri(BaseUrl + "post/show/" + id), + string.IsNullOrWhiteSpace(sampleUrl) ? null : new Uri(sampleUrl), + GetRating(elem["rating"].Value()[0]), + elem["tags"].Value().Split(' '), + null, + id, + elem["file_size"].Value(), + elem["height"].Value(), + elem["width"].Value(), + elem["preview_height"].Value(), + elem["preview_width"].Value(), + _unixTime.AddSeconds(elem["created_at"].Value()), + elem["source"].Value(), + elem["score"].Value(), + elem["md5"].Value() + ); + } + + private protected override Search.Post.SearchResult[] GetPostsSearchResult(object json) + { + return json is JArray array + ? array.Select(GetPostSearchResult).ToArray() + : Array.Empty(); + } + + private protected override Search.Comment.SearchResult GetCommentSearchResult(object json) + { + var elem = (JObject)json; + return new Search.Comment.SearchResult( + elem["id"].Value(), + elem["post_id"].Value(), + elem["creator_id"].Value(), + elem["created_at"].Value(), + elem["creator"].Value(), + elem["body"].Value() + ); + } + + private protected override Search.Wiki.SearchResult GetWikiSearchResult(object json) + { + var elem = (JObject)json; + return new Search.Wiki.SearchResult( + elem["id"].Value(), + elem["title"].Value(), + elem["created_at"].Value(), + elem["updated_at"].Value(), + elem["body"].Value() + ); + } + + private protected override Search.Tag.SearchResult GetTagSearchResult(object json) + { + var elem = (JObject)json; + return new Search.Tag.SearchResult( + elem["id"].Value(), + elem["name"].Value(), + (Search.Tag.TagType)elem["type"].Value(), + elem["count"].Value() + ); + } + + private protected override Search.Related.SearchResult GetRelatedSearchResult(object json) + { + var elem = (JArray)json; + return new Search.Related.SearchResult( + elem[0].Value(), + elem[1].Value() + ); + } + */ + } +} diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index c8366f9..66aa37d 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -6,7 +6,6 @@ using System.Text.Json; using System.Threading.Tasks; using System.Web; -using static BooruSharp.Booru.Template.E621; namespace BooruSharp.Booru.Template { diff --git a/README.md b/README.md index a9fc182..e5e5b3e 100644 --- a/README.md +++ b/README.md @@ -44,26 +44,27 @@ If you have any question, feel free to [contact me](#need-more-help) ## Features availability -| Booru | Multiple Random Images | Post by ID | Post by MD5 | Tag by ID | Comment API | Last Comments API | Wiki API | Related Tag API | Post Count API | Favorite API | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -| Atfbooru | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | -| Danbooru Donmai | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | -| Derpibooru | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | -| E621 | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| E926 | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | -| Gelbooru | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | -| Konachan | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | -| Lolibooru | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | -| Ponybooru | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | -| Realbooru | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | -| Rule 34 | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | -| Safebooru | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | -| Sakugabooru | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | -| Sankaku Complex | ✔️ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | -| Twibooru | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | -| Xbooru | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | -| Yandere | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | -| Pixiv | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | +| Booru | Search with no Tag | Multiple Random Images | Post by ID | Post by MD5 | Tag by ID | Comment API | Last Comments API | Wiki API | Related Tag API | Post Count API | Favorite API | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| Atfbooru | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | +| Danbooru Donmai | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ❌ | +| Derpibooru | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | +| E621 | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| E926 | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Furrybooru | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | +| Gelbooru | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | +| Konachan | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | +| Lolibooru | ✔️ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ✔️ | ❌ | +| Ponybooru | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | +| Realbooru | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | +| Rule 34 | ❌ | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | +| Safebooru | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | +| Sakugabooru | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | +| Sankaku Complex | ✔️ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ❌ | +| Twibooru | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ✔️ | ❌ | +| Xbooru | ✔️ | ❌ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | ❌ | ✔️ | ✔️ | +| Yandere | ✔️ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ❌ | ✔️ | ✔️ | ✔️ | ❌ | +| Pixiv | ❌ | ❌ | ✔️ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️ | ✔️ | ### Additional notes on Pixiv Pixiv also have 2 methods to download an image (ImageToByteArrayAsync) or a preview (PreviewToByteArrayAsync), it'll give you a byte array given a SearchResult From eaa4d5b7e7173b48874337565811cccc01b6057d Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 00:56:57 +0100 Subject: [PATCH 18/56] Rework code structure --- BooruSharp.UnitTests/Utils.cs | 8 +++--- BooruSharp/Booru/ABooru.cs | 26 +++++++++++------- BooruSharp/Booru/IBooru.cs | 33 ----------------------- BooruSharp/Booru/Template/BooruOnRails.cs | 16 +++-------- BooruSharp/Booru/Template/Danbooru.cs | 9 ++++--- BooruSharp/Booru/Template/E621.cs | 9 ++++--- BooruSharp/Booru/Template/Gelbooru.cs | 9 ++++--- BooruSharp/Booru/Template/Gelbooru02.cs | 9 ++++--- BooruSharp/Booru/Template/Moebooru.cs | 9 ++++--- BooruSharp/Booru/Template/MyImouto.cs | 9 ++++--- BooruSharp/Booru/Template/Philomena.cs | 16 +++-------- BooruSharp/Booru/Template/Sankaku.cs | 9 ++++--- BooruSharp/Search/Post/ABooru.cs | 5 ++-- 13 files changed, 66 insertions(+), 101 deletions(-) delete mode 100644 BooruSharp/Booru/IBooru.cs diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 6e802d2..40f82fd 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -30,9 +30,9 @@ public static class Utils //new BooruTestData() { BooruType = typeof(Pixiv) } }; - private static readonly Dictionary> _boorus = new(); + private static readonly Dictionary> _boorus = new(); - public static Task GetAsync(Type type) + public static Task GetAsync(Type type) { lock (_boorus) { @@ -46,9 +46,9 @@ public static Task GetAsync(Type type) } } - private static async Task CreateBooruAsync(Type type) + private static async Task CreateBooruAsync(Type type) { - var booru = (IBooru)Activator.CreateInstance(type); + var booru = (ABooru)Activator.CreateInstance(type); /*if (booru is Pixiv pixiv) { diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 86a5d49..66d5757 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -12,26 +12,26 @@ namespace BooruSharp.Booru /// /// Defines basic capabilities of a booru. This class is . /// - public abstract partial class ABooru : IBooru + public abstract partial class ABooru { public virtual bool CanSearchWithNoTag => true; /// public abstract bool IsSafe { get; } - private protected virtual Search.Comment.SearchResult GetCommentSearchResult(TComment parsingData) + private protected virtual Search.Comment.SearchResult GetCommentSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); - private protected virtual PostSearchResult GetPostSearchResult(TPost parsingData) + private protected virtual Task GetPostSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); - private protected virtual Search.Related.SearchResult GetRelatedSearchResult(TRelated parsingData) + private protected virtual Search.Related.SearchResult GetRelatedSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); - private protected virtual Search.Tag.TagSearchResult GetTagSearchResult(TTag parsingData) + private protected virtual Search.Tag.TagSearchResult GetTagSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); - private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(TWiki parsingData) + private protected virtual Search.Wiki.SearchResult GetWikiSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); /// @@ -60,12 +60,20 @@ protected virtual void PreRequest(HttpRequestMessage message) /// List of tags sent by the user protected abstract Task CreateRandomPostUriAsync(string[] tags); - protected virtual async Task GetPostFromUriAsync(Uri url) + protected virtual async Task GetDataArray(Uri url) { - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + return JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions { PropertyNamingPolicy = new SnakeCaseNamingPolicy() - }).FirstOrDefault()); + }); + } + + protected virtual async Task GetDataAsync(Uri url) + { + return JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }); } /// diff --git a/BooruSharp/Booru/IBooru.cs b/BooruSharp/Booru/IBooru.cs deleted file mode 100644 index 3c219ee..0000000 --- a/BooruSharp/Booru/IBooru.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BooruSharp.Search.Post; -using System.Net.Http; -using System.Threading.Tasks; - -namespace BooruSharp.Booru -{ - public interface IBooru - { - public bool CanSearchWithNoTag { get; } - - /// - /// Gets whether this booru is considered safe (that is, all posts on - /// this booru have rating of ). - /// - public abstract bool IsSafe { get; } - - /// - /// Checks for the booru availability. - /// Throws if service isn't available. - /// - public Task CheckAvailabilityAsync(); - - /// - /// Searches for a random post. If array is specified - /// and isn't empty, random post containing those tags will be returned. - /// - /// The optional array of tags that must be contained in the post. - /// The task object representing the asynchronous operation. - /// - /// - public Task GetRandomPostAsync(params string[] tagsArg); - } -} diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index cf4a9d2..0b911d0 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -1,9 +1,7 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; -using System.Text.Json; using System.Threading.Tasks; using System.Web; @@ -12,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Booru-on-rails https://github.com/derpibooru/booru-on-rails . This class is . /// - public abstract class BooruOnRails : ABooru + public abstract class BooruOnRails : ABooru { /// /// Initializes a new instance of the template class. @@ -58,16 +56,10 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } - protected override async Task GetPostFromUriAsync(Uri url) + private protected override async Task GetPostSearchResultAsync(Uri uri) { - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions - { - PropertyNamingPolicy = new SnakeCaseNamingPolicy() - }).Posts.FirstOrDefault()); - } + var parsingData = (await GetDataAsync(uri)).Posts[0]; - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) - { Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 16c177e..8e28318 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; @@ -11,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Danbooru https://github.com/danbooru/danbooru . This class is . /// - public abstract class Danbooru : ABooru + public abstract class Danbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -50,8 +49,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, previewUrl: parsingData.PreviewFileUrl != null ? new Uri(parsingData.PreviewFileUrl) : null, diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 65eb1af..b318bd9 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; @@ -12,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on E621. This class is . /// - public abstract class E621 : ABooru + public abstract class E621 : ABooru { /// /// Initializes a new instance of the template class. @@ -57,8 +56,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: parsingData.Url.Url != null ? new Uri(parsingData.Url.Url) : null, previewUrl: parsingData.Preview.Url != null ? new Uri(parsingData.Preview.Url) : null, diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 66c65ed..d47fadf 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Globalization; using System.Linq; @@ -12,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Gelbooru. This class is . /// - public abstract class Gelbooru : ABooru + public abstract class Gelbooru : ABooru { /// /// Initializes a new instance of the template class. @@ -44,8 +43,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index dde6901..6a65907 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -13,7 +12,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Gelbooru 0.2. This class is . /// - public abstract class Gelbooru02 : ABooru + public abstract class Gelbooru02 : ABooru { /// /// Initializes a new instance of the template class. @@ -59,8 +58,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index da11695..0a10801 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -11,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Moebooru. This class is . /// - public abstract class Moebooru : ABooru + public abstract class Moebooru : ABooru { /// /// Initializes a new instance of the template class. @@ -43,8 +42,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index 8bbc4b3..ca69891 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -11,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on MyImouto. This class is . /// - public abstract class MyImouto : ABooru + public abstract class MyImouto : ABooru { /// /// Initializes a new instance of the template class. @@ -43,8 +42,10 @@ protected override void PreRequest(HttpRequestMessage message) } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 66aa37d..d4d6393 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,9 +1,7 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; -using System.Text.Json; using System.Threading.Tasks; using System.Web; @@ -12,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Philomena https://github.com/ZizzyDizzyMC/philomena . This class is . /// - public abstract class Philomena : ABooru + public abstract class Philomena : ABooru { /// /// Initializes a new instance of the template class. @@ -62,16 +60,10 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } - protected override async Task GetPostFromUriAsync(Uri url) + private protected override async Task GetPostSearchResultAsync(Uri uri) { - return GetPostSearchResult(JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions - { - PropertyNamingPolicy = new SnakeCaseNamingPolicy() - }).Images.FirstOrDefault()); - } + var parsingData = (await GetDataAsync(uri)).Images[0]; - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) - { Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 2ace36f..97657b0 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -1,5 +1,4 @@ -using BooruSharp.Booru.Parsing; -using BooruSharp.Search.Post; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; @@ -12,7 +11,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Sankaku. This class is . /// - public abstract class Sankaku : ABooru + public abstract class Sankaku : ABooru { /// /// Initializes a new instance of the template class. @@ -48,8 +47,10 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't } } - private protected override PostSearchResult GetPostSearchResult(SearchResult parsingData) + private protected override async Task GetPostSearchResultAsync(Uri uri) { + var parsingData = (await GetDataAsync(uri)); + return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, previewUrl: parsingData.PreviewUrl != null ? new(parsingData.PreviewUrl) : null, diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 0e6cb6e..1128cfe 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -1,12 +1,11 @@ using BooruSharp.Search.Post; using System; using System.Linq; -using System.Text.Json; using System.Threading.Tasks; namespace BooruSharp.Booru { - public abstract partial class ABooru : IBooru + public abstract partial class ABooru { /// /// Converts a letter to its matching . @@ -31,7 +30,7 @@ public async Task GetRandomPostAsync(params string[] tagsArg) : Array.Empty(); var url = await CreateRandomPostUriAsync(tags); - return await GetPostFromUriAsync(url); + return await GetPostSearchResultAsync(url); } /* private const int _limitedTagsSearchCount = 2; From 4edd3a33cd782f65dedd3b1df6aea4c129f8d037 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 01:01:20 +0100 Subject: [PATCH 19/56] Clean dupplicate code --- BooruSharp/Booru/Template/MyImouto.cs | 21 +------ BooruSharp/Booru/Template/Philomena.cs | 81 +------------------------- 2 files changed, 2 insertions(+), 100 deletions(-) diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index ca69891..284e3a8 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -10,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on MyImouto. This class is . /// - public abstract class MyImouto : ABooru + public abstract class MyImouto : Moebooru { /// /// Initializes a new instance of the template class. @@ -23,25 +23,6 @@ protected MyImouto(string domain) : base(domain) { } - protected override Uri CreateQueryString(string query, string squery = "index") - { - return new($"{BaseUrl}{query}/{squery}.json"); - } - - protected override Task CreateRandomPostUriAsync(string[] tags) - { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); - } - - /// - protected override void PreRequest(HttpRequestMessage message) - { - if (Auth != null) - { - message.Headers.Add("Cookie", "user_id=" + Auth.UserId + ";pass_hash=" + Auth.PasswordHash); - } - } - private protected override async Task GetPostSearchResultAsync(Uri uri) { var parsingData = (await GetDataAsync(uri)); diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index d4d6393..23ac4b1 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -10,7 +10,7 @@ namespace BooruSharp.Booru.Template /// /// Template booru based on Philomena https://github.com/ZizzyDizzyMC/philomena . This class is . /// - public abstract class Philomena : ABooru + public abstract class Philomena : BooruOnRails { /// /// Initializes a new instance of the template class. @@ -32,90 +32,11 @@ protected override Uri CreateQueryString(string query, string squery = "index") return new($"{BaseUrl}api/v1/json/search/{query}s"); } - protected override Task CreateRandomPostUriAsync(string[] tags) - { - if (!tags.Any()) - { - return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=id.gte:0", "sf=random")); - } - return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "sf=random")); - } - - /// - /// ID used to set filter and have access to as many posts as possible - /// - protected abstract int FilterID { get; } - - /// - protected override void PreRequest(HttpRequestMessage message) - { - var uriBuilder = new UriBuilder(message.RequestUri.AbsoluteUri); - var query = HttpUtility.ParseQueryString(uriBuilder.Query); - query["filter_id"] = $"{FilterID}"; - if (Auth != null) - { - query["key"] = Auth.PasswordHash; - } - uriBuilder.Query = query.ToString(); - message.RequestUri = new Uri(uriBuilder.ToString()); - } - - private protected override async Task GetPostSearchResultAsync(Uri uri) - { - var parsingData = (await GetDataAsync(uri)).Images[0]; - - Rating rating; - if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; - else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; - else if (parsingData.Tags.Contains("suggestive")) rating = Rating.Safe; - else if (parsingData.Tags.Contains("safe")) rating = Rating.General; - else rating = (Rating)(-1); // Some images doesn't have a rating - return new PostSearchResult( - fileUrl: new(parsingData.Representations.Full), - previewUrl: null, - postUrl: new($"{BaseUrl}images/{parsingData.Id}"), - sampleUri: new(parsingData.Representations.Thumb), - rating: rating, - tags: parsingData.Tags, - detailedTags: null, - id: parsingData.Id, - size: parsingData.Size, - height: parsingData.Height, - width: parsingData.Width, - previewHeight: null, - previewWidth: null, - creation: parsingData.CreatedAt, - sources: string.IsNullOrEmpty(parsingData.SourceUrl) ? Array.Empty() : new[] { parsingData.SourceUrl }, - score: parsingData.Score, - hash: parsingData.Sha512Hash - ); - } - public class PostContainer { public SearchResult[] Images { init; get; } } - public class SearchResult - { - public Representations Representations { init; get; } - public int Id { init; get; } - public string[] Tags { init; get; } - public int Size { init; get; } - public int Width { init; get; } - public int Height { init; get; } - public DateTime CreatedAt { init; get; } - public string SourceUrl { init; get; } - public int Score { init; get; } - public string Sha512Hash { init; get; } - } - - public class Representations - { - public string Full { init; get; } - public string Thumb { init; get; } - } - /* private protected override JToken ParseFirstPostSearchResult(object json) { From 4049526f99d1a2eb8ad46fd6a1affd436b915b39 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 01:05:57 +0100 Subject: [PATCH 20/56] Fix rule34 --- BooruSharp/Booru/Impl/Rule34.cs | 39 +++++++++++++++++++++++++ BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/E621.cs | 2 +- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- BooruSharp/Booru/Template/Moebooru.cs | 2 +- BooruSharp/Booru/Template/MyImouto.cs | 2 +- BooruSharp/Booru/Template/Sankaku.cs | 2 +- 8 files changed, 46 insertions(+), 7 deletions(-) diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 5cfd739..676c1c4 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -3,6 +3,7 @@ using System.Xml; using System; using BooruSharp.Search; +using BooruSharp.Search.Post; namespace BooruSharp.Booru { @@ -43,5 +44,43 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) /// public override bool IsSafe => false; + + private protected override async Task GetPostSearchResultAsync(Uri uri) + { + var parsingData = await GetDataAsync(uri); + + return new PostSearchResult( + fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample == 1 ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split(), + detailedTags: null, + id: parsingData.Id, + size: null, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: null, + sources: null, + score: parsingData.Score, + hash: null + ); + } + + public class SearchResult + { + public string Directory { init; get; } + public string Image { init; get; } + public int Id { init; get; } + public int Sample { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int? Score { init; get; } + } } } diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 8e28318..c3cf95d 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -51,7 +51,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index b318bd9..be78d2a 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -58,7 +58,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: parsingData.Url.Url != null ? new Uri(parsingData.Url.Url) : null, diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index d47fadf..b62a381 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -45,7 +45,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 6a65907..c3d6f2f 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -60,7 +60,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 0a10801..4b3391f 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -44,7 +44,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index 284e3a8..bfe2d38 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -25,7 +25,7 @@ protected MyImouto(string domain) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 97657b0..d4b7d7a 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -49,7 +49,7 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)); + var parsingData = await GetDataAsync(uri); return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, From 95884bcb4ab9222de216d1d969e6a114001100ae Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 01:38:13 +0100 Subject: [PATCH 21/56] Fix unit tests --- BooruSharp/Booru/Impl/Furrybooru.cs | 12 +++++-- BooruSharp/Booru/Impl/Lolibooru.cs | 2 +- BooruSharp/Booru/Impl/Realbooru.cs | 44 ++++++++++++++++++++++++- BooruSharp/Booru/Impl/Rule34.cs | 8 ++--- BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/E621.cs | 19 +++++++---- BooruSharp/Booru/Template/Gelbooru.cs | 7 +++- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- BooruSharp/Booru/Template/Moebooru.cs | 2 +- BooruSharp/Booru/Template/MyImouto.cs | 2 +- BooruSharp/Booru/Template/Philomena.cs | 31 +++++++++++++++++ BooruSharp/Booru/Template/Sankaku.cs | 6 ++-- BooruSharp/Search/Post/ABooru.cs | 8 ++++- 13 files changed, 120 insertions(+), 25 deletions(-) diff --git a/BooruSharp/Booru/Impl/Furrybooru.cs b/BooruSharp/Booru/Impl/Furrybooru.cs index 8b4bd60..77c4a28 100644 --- a/BooruSharp/Booru/Impl/Furrybooru.cs +++ b/BooruSharp/Booru/Impl/Furrybooru.cs @@ -1,10 +1,16 @@ -namespace BooruSharp.Booru +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using System.Web; +using System.Xml; +using System; + +namespace BooruSharp.Booru { /// /// FurryBooru. /// https://furry.booru.org/ /// - //[Obsolete("Furrybooru does no longer works, please consider using E621/E926 instead", error: true)] // TODO: Looks like it's fixed public sealed class Furrybooru : Template.Gelbooru02 { /// @@ -16,5 +22,7 @@ public Furrybooru() /// public override bool IsSafe => false; + + public override bool CanSearchWithNoTag => false; } } diff --git a/BooruSharp/Booru/Impl/Lolibooru.cs b/BooruSharp/Booru/Impl/Lolibooru.cs index 1844486..743d4e8 100644 --- a/BooruSharp/Booru/Impl/Lolibooru.cs +++ b/BooruSharp/Booru/Impl/Lolibooru.cs @@ -4,7 +4,7 @@ /// Lolibooru. /// https://lolibooru.moe/ /// - public sealed class Lolibooru : Template.Moebooru + public sealed class Lolibooru : Template.MyImouto { /// /// Initializes a new instance of the class. diff --git a/BooruSharp/Booru/Impl/Realbooru.cs b/BooruSharp/Booru/Impl/Realbooru.cs index 1f3e0e1..04dcd08 100644 --- a/BooruSharp/Booru/Impl/Realbooru.cs +++ b/BooruSharp/Booru/Impl/Realbooru.cs @@ -1,4 +1,8 @@ -namespace BooruSharp.Booru +using BooruSharp.Search.Post; +using System.Threading.Tasks; +using System; + +namespace BooruSharp.Booru { /// /// Realbooru. @@ -15,5 +19,43 @@ public Realbooru() /// public override bool IsSafe => false; + + private protected override async Task GetPostSearchResultAsync(Uri uri) + { + var parsingData = (await GetDataAsync(uri))[0]; + + return new PostSearchResult( + fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample == 1 ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + rating: GetRating(parsingData.Rating[0]), + tags: parsingData.Tags.Split(), + detailedTags: null, + id: parsingData.Id, + size: null, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: null, + sources: null, + score: parsingData.Score, + hash: null + ); + } + + public class SearchResult + { + public string Directory { init; get; } + public string Image { init; get; } + public int Id { init; get; } + public int Sample { init; get; } + public string Rating { init; get; } + public string Tags { init; get; } + public int Height { init; get; } + public int Width { init; get; } + public int? Score { init; get; } + } } } diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 676c1c4..3b706ad 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -24,10 +24,8 @@ public Rule34() protected override async Task CreateRandomPostUriAsync(string[] tags) { - if (!tags.Any()) - { - throw new FeatureUnavailable("Rule34 doesn't support searchs with no tag"); - } + // We don't have to handle what happen when there is no tag because it'll throw before + var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); @@ -47,7 +45,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index c3cf95d..bc494f6 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -51,7 +51,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index be78d2a..a63c3e2 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -58,10 +58,10 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri)).Posts[0]; return new PostSearchResult( - fileUrl: parsingData.Url.Url != null ? new Uri(parsingData.Url.Url) : null, + fileUrl: parsingData.File.Url != null ? new Uri(parsingData.File.Url) : null, previewUrl: parsingData.Preview.Url != null ? new Uri(parsingData.Preview.Url) : null, postUrl: new Uri($"{BaseUrl}posts/{parsingData.Id}"), sampleUri: parsingData.Sample.Url != null ? new Uri(parsingData.Sample.Url) : null, @@ -80,21 +80,26 @@ private protected override async Task GetPostSearchResultAsync .Concat(parsingData.Tags.Lore.Select(x => new TagSearchResult(-1, x, TagType.Lore, -1))) .Concat(parsingData.Tags.Meta.Select(x => new TagSearchResult(-1, x, TagType.Metadata, -1))), id: parsingData.Id, - size: parsingData.Url.Size, - height: parsingData.Url.Height, - width: parsingData.Url.Width, + size: parsingData.File.Size, + height: parsingData.File.Height, + width: parsingData.File.Width, previewHeight: parsingData.Preview.Height, previewWidth: parsingData.Preview.Width, creation: parsingData.CreatedAt, sources: parsingData.Sources ?? Array.Empty(), score: parsingData.Score.Total, - hash: parsingData.Url.Md5 + hash: parsingData.File.Md5 ); } + public class DataContainer + { + public SearchResult[] Posts { init; get; } + } + public class SearchResult { - public ImageData Url { init; get; } + public ImageData File { init; get; } public ImageData Preview { init; get; } public ImageData Sample { init; get; } public Tags Tags { init; get; } diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index b62a381..af90338 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -45,7 +45,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri)).Post[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), @@ -68,6 +68,11 @@ private protected override async Task GetPostSearchResultAsync ); } + public class DataContainer + { + public SearchResult[] Post { init; get; } + } + public class SearchResult { public string FileUrl { init; get; } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index c3d6f2f..9db31c7 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -60,7 +60,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 4b3391f..489cc76 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -44,7 +44,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index bfe2d38..d8beaa5 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -25,7 +25,7 @@ protected MyImouto(string domain) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 23ac4b1..ec294f4 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -32,6 +32,37 @@ protected override Uri CreateQueryString(string query, string squery = "index") return new($"{BaseUrl}api/v1/json/search/{query}s"); } + private protected override async Task GetPostSearchResultAsync(Uri uri) + { + var parsingData = (await GetDataAsync(uri)).Images[0]; + + Rating rating; + if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; + else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; + else if (parsingData.Tags.Contains("suggestive")) rating = Rating.Safe; + else if (parsingData.Tags.Contains("safe")) rating = Rating.General; + else rating = (Rating)(-1); // Some images doesn't have a rating + return new PostSearchResult( + fileUrl: new(parsingData.Representations.Full), + previewUrl: null, + postUrl: new($"{BaseUrl}images/{parsingData.Id}"), + sampleUri: new(parsingData.Representations.Thumb), + rating: rating, + tags: parsingData.Tags, + detailedTags: null, + id: parsingData.Id, + size: parsingData.Size, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: parsingData.CreatedAt, + sources: string.IsNullOrEmpty(parsingData.SourceUrl) ? Array.Empty() : new[] { parsingData.SourceUrl }, + score: parsingData.Score, + hash: parsingData.Sha512Hash + ); + } + public class PostContainer { public SearchResult[] Images { init; get; } diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index d4b7d7a..712a663 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -49,7 +49,7 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = await GetDataAsync(uri); + var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, @@ -87,8 +87,8 @@ public class SearchResult public int FileSize { init; get; } public int Height { init; get; } public int Width { init; get; } - public int PreviewHeight { init; get; } - public int PreviewWidth { init; get; } + public int? PreviewHeight { init; get; } + public int? PreviewWidth { init; get; } public CreationInfo CreatedAt { init; get; } public string Source { init; get; } public int TotalScore { init; get; } diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 1128cfe..3a3f978 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Threading.Tasks; @@ -29,6 +30,11 @@ public async Task GetRandomPostAsync(params string[] tagsArg) ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() : Array.Empty(); + if (!tags.Any() && !CanSearchWithNoTag) + { + throw new FeatureUnavailable("This booru doesn't support search with no tag"); + } + var url = await CreateRandomPostUriAsync(tags); return await GetPostSearchResultAsync(url); } From 905492d7261a13ae6b9b647ea32b6367fd33dfcb Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 01:45:50 +0100 Subject: [PATCH 22/56] Remove unused usings --- BooruSharp/Booru/Template/Philomena.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index ec294f4..888a465 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,9 +1,7 @@ using BooruSharp.Search.Post; using System; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; -using System.Web; namespace BooruSharp.Booru.Template { From bfb3988e16fcaf81c38a202f6b1f77579a6d6dec Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 8 Dec 2022 01:46:24 +0100 Subject: [PATCH 23/56] Remove unused code --- BooruSharp/Booru/Parsing/EmptyParsing.cs | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 BooruSharp/Booru/Parsing/EmptyParsing.cs diff --git a/BooruSharp/Booru/Parsing/EmptyParsing.cs b/BooruSharp/Booru/Parsing/EmptyParsing.cs deleted file mode 100644 index d6a8375..0000000 --- a/BooruSharp/Booru/Parsing/EmptyParsing.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace BooruSharp.Booru.Parsing -{ - public class EmptyParsing - { } -} From f93356491d712f9b328cf545ea30855d0f22fc05 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 04:12:17 +0100 Subject: [PATCH 24/56] Improve unit tests --- BooruSharp.UnitTests/BooruTests.cs | 6 ++-- BooruSharp.UnitTests/TestsFixture.cs | 11 +++++++ BooruSharp.UnitTests/Utils.cs | 38 +++++++++++++++++++++++ BooruSharp/Booru/ABooru.cs | 1 - BooruSharp/Booru/Template/BooruOnRails.cs | 5 +-- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- BooruSharp/Booru/Template/MyImouto.cs | 1 - BooruSharp/Booru/Template/Philomena.cs | 4 +-- BooruSharp/Booru/Template/Sankaku.cs | 1 - 9 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 BooruSharp.UnitTests/TestsFixture.cs diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index cb90fd0..e4496da 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,11 +1,12 @@ using BooruSharp.Search; +using System; using System.Collections.Generic; using System.Threading.Tasks; using Xunit; namespace BooruSharp.UnitTests { - public class BooruTests + public class BooruTests : IClassFixture { // Because of compile error public static IEnumerable BooruParams => Utils.BooruParams; @@ -17,7 +18,8 @@ public async Task GetRandomImageAsync(BooruTestData data) var booru = await Utils.GetAsync(data.BooruType); if (booru.CanSearchWithNoTag) { - await booru.GetRandomPostAsync(); + var res = await booru.GetRandomPostAsync(); + await Utils.ValidatePostAsync(res, Array.Empty()); } else { diff --git a/BooruSharp.UnitTests/TestsFixture.cs b/BooruSharp.UnitTests/TestsFixture.cs new file mode 100644 index 0000000..471b546 --- /dev/null +++ b/BooruSharp.UnitTests/TestsFixture.cs @@ -0,0 +1,11 @@ +namespace BooruSharp.UnitTests +{ + public class TestsFixture + { + public TestsFixture() + { + Utils.Client = new(); + Utils.Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 BooruSharp"); + } + } +} diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 40f82fd..ec13af0 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -1,7 +1,11 @@ using BooruSharp.Booru; +using BooruSharp.Search.Post; using System; using System.Collections.Generic; +using System.Linq; +using System.Net.Http; using System.Threading.Tasks; +using Xunit; namespace BooruSharp.UnitTests { @@ -30,6 +34,8 @@ public static class Utils //new BooruTestData() { BooruType = typeof(Pixiv) } }; + public static HttpClient Client; + private static readonly Dictionary> _boorus = new(); public static Task GetAsync(Type type) @@ -61,5 +67,37 @@ private static async Task CreateBooruAsync(Type type) return booru; } + + private static async Task ValidateUrlAsync(string url) + { + if (string.IsNullOrEmpty(url)) + { + return false; + } + + var response = await Client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url)); + return response.IsSuccessStatusCode; + } + + public static async Task ValidatePostAsync(PostSearchResult res, string[] inputTags) + { + Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri}"); + Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri}"); + Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri}"); + if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri}"); + Assert.InRange(res.Rating, Rating.General, Rating.Explicit); + Assert.NotEmpty(res.Tags); + Assert.NotEqual(0, res.ID); + Assert.NotEqual(0, res.Width); + Assert.NotEqual(0, res.Height); + if (res.PreviewWidth != null) Assert.NotEqual(0, res.PreviewWidth); + if (res.PreviewHeight != null) Assert.NotEqual(0, res.PreviewHeight); + if (res.Size != null) Assert.NotEqual(0, res.Size); + foreach (var tag in inputTags) + { + Assert.Contains(tag, res.Tags); + if (res.DetailedTags != null) Assert.Contains(tag, res.DetailedTags.Select(x => x.Name)); + } + } } } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 66d5757..bae9397 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -1,7 +1,6 @@ using BooruSharp.Search; using BooruSharp.Search.Post; using System; -using System.Linq; using System.Net; using System.Net.Http; using System.Text.Json; diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 0b911d0..eeca9c2 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -68,9 +68,9 @@ private protected override async Task GetPostSearchResultAsync else rating = (Rating)(-1); // Some images doesn't have a rating return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), - previewUrl: null, + previewUrl: new(parsingData.Representations.Thumb), postUrl: new($"{BaseUrl}images/{parsingData.Id}"), - sampleUri: new(parsingData.Representations.Thumb), + sampleUri: new(parsingData.Representations.Large), rating: rating, tags: parsingData.Tags, detailedTags: null, @@ -109,6 +109,7 @@ public class SearchResult public class Representations { public string Full { init; get; } + public string Large { init; get; } public string Thumb { init; get; } } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 9db31c7..abfa6ba 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -64,7 +64,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), sampleUri: parsingData.Sample ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, rating: GetRating(parsingData.Rating[0]), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index d8beaa5..4b91dd3 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -1,7 +1,6 @@ using BooruSharp.Search.Post; using System; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using System.Web; diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 888a465..a84b06b 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -42,9 +42,9 @@ private protected override async Task GetPostSearchResultAsync else rating = (Rating)(-1); // Some images doesn't have a rating return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), - previewUrl: null, + previewUrl: new(parsingData.Representations.Thumb), postUrl: new($"{BaseUrl}images/{parsingData.Id}"), - sampleUri: new(parsingData.Representations.Thumb), + sampleUri: new(parsingData.Representations.Large), rating: rating, tags: parsingData.Tags, detailedTags: null, diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 712a663..bab4225 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using System.Web; namespace BooruSharp.Booru.Template { From 532149c4267ffa48b1282a6a40403c123271e4d8 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 04:27:07 +0100 Subject: [PATCH 25/56] Fix bugs --- BooruSharp.UnitTests/BooruTests.cs | 3 +++ BooruSharp/Booru/Template/E621.cs | 2 +- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 4 ++-- BooruSharp/Booru/Template/Moebooru.cs | 2 +- BooruSharp/Booru/Template/Sankaku.cs | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index e4496da..37bfca2 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -19,7 +19,10 @@ public async Task GetRandomImageAsync(BooruTestData data) if (booru.CanSearchWithNoTag) { var res = await booru.GetRandomPostAsync(); + var res2 = await booru.GetRandomPostAsync(); await Utils.ValidatePostAsync(res, Array.Empty()); + await Utils.ValidatePostAsync(res2, Array.Empty()); + Assert.NotEqual(res.ID, res2.ID); } else { diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index a63c3e2..b836260 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -43,7 +43,7 @@ protected override Task CreateRandomPostUriAsync(string[] tags) { throw new Search.TooManyTags(); } - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "random=true")); + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); } /// diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index af90338..340c3dd 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -31,7 +31,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+sort:random")); + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+sort:random")); } /// diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index abfa6ba..5e44de5 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -46,7 +46,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) if (max == 0) throw new Search.InvalidTags(); - return CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); + return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); } /// @@ -66,7 +66,7 @@ private protected override async Task GetPostSearchResultAsync fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + sampleUri: parsingData.Sample ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 489cc76..a5d5fdb 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -30,7 +30,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); } /// diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index bab4225..6b445a8 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -34,7 +34,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); } /// From a45c32d70cfe35584b2ede1ee29751afc30a5dbb Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 15:07:18 +0100 Subject: [PATCH 26/56] Fix unit tests failures --- BooruSharp.UnitTests/Utils.cs | 6 +++--- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index ec13af0..126e8a9 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -81,9 +81,9 @@ private static async Task ValidateUrlAsync(string url) public static async Task ValidatePostAsync(PostSearchResult res, string[] inputTags) { - Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri}"); - Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri}"); - Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri}"); + if (res.FileUrl != null) Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri}"); + if (res.PreviewUrl != null) Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri}"); + if (res.PostUrl != null) Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri}"); if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri}"); Assert.InRange(res.Rating, Rating.General, Rating.Explicit); Assert.NotEmpty(res.Tags); diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 340c3dd..c75cfe5 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -51,7 +51,7 @@ private protected override async Task GetPostSearchResultAsync fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), postUrl: new Uri($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, + sampleUri: !string.IsNullOrEmpty(parsingData.SampleUrl) ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), detailedTags: null, From 16818425f2d7189c98612f9cbd0ce16b8ac8950f Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 15:26:51 +0100 Subject: [PATCH 27/56] Fix more unit tests --- BooruSharp/Booru/ABooru.cs | 11 ++++++++--- BooruSharp/Booru/Impl/Realbooru.cs | 8 ++++---- BooruSharp/Booru/Impl/Rule34.cs | 10 ++++++---- BooruSharp/Booru/Impl/SankakuComplex.cs | 6 +++++- BooruSharp/Booru/Impl/Xbooru.cs | 6 +++++- BooruSharp/Booru/Template/BooruOnRails.cs | 4 ++-- BooruSharp/Booru/Template/Danbooru.cs | 8 ++++---- BooruSharp/Booru/Template/E621.cs | 8 ++++---- BooruSharp/Booru/Template/Gelbooru.cs | 4 ++-- BooruSharp/Booru/Template/Gelbooru02.cs | 12 ++++++------ BooruSharp/Booru/Template/Moebooru.cs | 4 ++-- BooruSharp/Booru/Template/MyImouto.cs | 2 +- BooruSharp/Booru/Template/Philomena.cs | 6 +++--- BooruSharp/Booru/Template/Sankaku.cs | 10 +++------- 14 files changed, 55 insertions(+), 44 deletions(-) diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index bae9397..7ac8093 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -15,6 +15,11 @@ public abstract partial class ABooru { public virtual bool CanSearchWithNoTag => true; + public virtual Uri FileBaseUrl => APIBaseUrl; + public virtual Uri PreviewBaseUrl => APIBaseUrl; + public virtual Uri PostBaseUrl => APIBaseUrl; + public virtual Uri SampleBaseUrl => APIBaseUrl; + /// public abstract bool IsSafe { get; } @@ -86,12 +91,12 @@ protected virtual async Task GetDataAsync(Uri url) /// /// The options to use. Use | (bitwise OR) operator to combine multiple options. /// - protected ABooru(string domain) + protected ABooru(string apiDomain) { Auth = null; HttpClient = null; - BaseUrl = new Uri("https://" + domain, UriKind.Absolute); + APIBaseUrl = new Uri("https://" + apiDomain, UriKind.Absolute); _imageUrl = CreateQueryString("post"); _tagUrl = CreateQueryString("tag"); @@ -187,7 +192,7 @@ protected get /// /// Gets the base API request URL. /// - public Uri BaseUrl { get; } + public Uri APIBaseUrl { get; } private HttpClient _client; protected readonly Uri _imageUrl, _tagUrl, _wikiUrl, _relatedUrl, _commentUrl; // URLs for differents endpoints diff --git a/BooruSharp/Booru/Impl/Realbooru.cs b/BooruSharp/Booru/Impl/Realbooru.cs index 04dcd08..7984d8f 100644 --- a/BooruSharp/Booru/Impl/Realbooru.cs +++ b/BooruSharp/Booru/Impl/Realbooru.cs @@ -25,10 +25,10 @@ private protected override async Task GetPostSearchResultAsync var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( - fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), - postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 3b706ad..7e14ddb 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -22,6 +22,8 @@ public Rule34() public override bool CanSearchWithNoTag => false; + public override Uri PostBaseUrl => new("https://rule34.xxx"); + protected override async Task CreateRandomPostUriAsync(string[] tags) { // We don't have to handle what happen when there is no tag because it'll throw before @@ -48,10 +50,10 @@ private protected override async Task GetPostSearchResultAsync var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( - fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), - postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), + postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Impl/SankakuComplex.cs b/BooruSharp/Booru/Impl/SankakuComplex.cs index 41c6a9b..db31745 100644 --- a/BooruSharp/Booru/Impl/SankakuComplex.cs +++ b/BooruSharp/Booru/Impl/SankakuComplex.cs @@ -1,4 +1,6 @@ -namespace BooruSharp.Booru +using System; + +namespace BooruSharp.Booru { /// /// Sankaku Complex. @@ -15,5 +17,7 @@ public SankakuComplex() /// public override bool IsSafe => false; + + public override Uri PostBaseUrl => new("https://beta.sankakucomplex.com"); } } diff --git a/BooruSharp/Booru/Impl/Xbooru.cs b/BooruSharp/Booru/Impl/Xbooru.cs index 8e91208..c28a2d9 100644 --- a/BooruSharp/Booru/Impl/Xbooru.cs +++ b/BooruSharp/Booru/Impl/Xbooru.cs @@ -1,4 +1,6 @@ -namespace BooruSharp.Booru +using System; + +namespace BooruSharp.Booru { /// /// Xbooru. @@ -15,5 +17,7 @@ public Xbooru() /// public override bool IsSafe => false; + + public override Uri PreviewBaseUrl => new("https://img.xbooru.com"); } } diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index eeca9c2..f0aa5ab 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -25,7 +25,7 @@ protected BooruOnRails(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}api/v3/search/{query}s"); + return new($"{APIBaseUrl}api/v3/search/{query}s"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -69,7 +69,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), previewUrl: new(parsingData.Representations.Thumb), - postUrl: new($"{BaseUrl}images/{parsingData.Id}"), + postUrl: new($"{PostBaseUrl}images/{parsingData.Id}"), sampleUri: new(parsingData.Representations.Large), rating: rating, tags: parsingData.Tags, diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index bc494f6..e714742 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -26,13 +26,13 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "tag" && squery == "related") { - return new($"{BaseUrl}related_tag.json"); + return new($"{APIBaseUrl}related_tag.json"); } if (query == "tag" && squery == "wiki") { - return new($"{BaseUrl}wiki_pages.json"); + return new($"{APIBaseUrl}wiki_pages.json"); } - return new($"{BaseUrl}{query}s.json"); + return new($"{APIBaseUrl}{query}s.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -56,7 +56,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, previewUrl: parsingData.PreviewFileUrl != null ? new Uri(parsingData.PreviewFileUrl) : null, - postUrl: parsingData.Id != null ? new Uri($"{BaseUrl}posts/{parsingData.Id}") : null, + postUrl: parsingData.Id != null ? new Uri($"{PostBaseUrl}posts/{parsingData.Id}") : null, sampleUri: parsingData.LargeFileUrl != null ? new Uri(parsingData.LargeFileUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.TagString.Split(), diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index b836260..2330821 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -28,13 +28,13 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "tag" && squery == "related") { - return new($"{BaseUrl}related_tag.json"); + return new($"{APIBaseUrl}related_tag.json"); } if (query == "tag" && squery == "wiki") { - return new($"{BaseUrl}wiki_pages.json"); + return new($"{APIBaseUrl}wiki_pages.json"); } - return new($"{BaseUrl}{query}s.json"); + return new($"{APIBaseUrl}{query}s.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -63,7 +63,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: parsingData.File.Url != null ? new Uri(parsingData.File.Url) : null, previewUrl: parsingData.Preview.Url != null ? new Uri(parsingData.Preview.Url) : null, - postUrl: new Uri($"{BaseUrl}posts/{parsingData.Id}"), + postUrl: new Uri($"{PostBaseUrl}posts/{parsingData.Id}"), sampleUri: parsingData.Sample.Url != null ? new Uri(parsingData.Sample.Url) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.General diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index c75cfe5..be63a2d 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -26,7 +26,7 @@ protected Gelbooru(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); + return new($"{APIBaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -50,7 +50,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), - postUrl: new Uri($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + postUrl: new Uri($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), sampleUri: !string.IsNullOrEmpty(parsingData.SampleUrl) ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 5e44de5..4759c9d 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -27,7 +27,7 @@ protected Gelbooru02(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); + return new($"{APIBaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); } protected override async Task CreateRandomPostUriAsync(string[] tags) @@ -35,7 +35,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) if (!tags.Any()) { // We need to request /index.php?page=post&s=random and get the id given by the redirect - HttpResponseMessage msg = await HttpClient.GetAsync($"{BaseUrl}index.php?page=post&s=random"); + HttpResponseMessage msg = await HttpClient.GetAsync($"{APIBaseUrl}index.php?page=post&s=random"); msg.EnsureSuccessStatusCode(); return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id")); } @@ -63,10 +63,10 @@ private protected override async Task GetPostSearchResultAsync var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( - fileUrl: new($"{BaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{BaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), - postUrl: new($"{BaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample ? new($"{BaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}") : null, + fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), + postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), + sampleUri: parsingData.Sample ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index a5d5fdb..6f92d8b 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -25,7 +25,7 @@ protected Moebooru(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{BaseUrl}{query}/{squery}.json"); + return new($"{APIBaseUrl}{query}/{squery}.json"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -49,7 +49,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), - postUrl: new Uri($"{BaseUrl}post/show/{parsingData.Id}"), + postUrl: new Uri($"{PostBaseUrl}post/show/{parsingData.Id}"), sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index 4b91dd3..388adc7 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -29,7 +29,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new(parsingData.FileUrl), previewUrl: new(parsingData.PreviewUrl), - postUrl: new Uri($"{BaseUrl}post/show/{parsingData.Id}"), + postUrl: new Uri($"{PostBaseUrl}post/show/{parsingData.Id}"), sampleUri: parsingData.SampleUrl != null ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split().Select(HttpUtility.HtmlDecode), diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index a84b06b..3fe6e15 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -25,9 +25,9 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "post") { - return new($"{BaseUrl}api/v1/json/search"); + return new($"{APIBaseUrl}api/v1/json/search"); } - return new($"{BaseUrl}api/v1/json/search/{query}s"); + return new($"{APIBaseUrl}api/v1/json/search/{query}s"); } private protected override async Task GetPostSearchResultAsync(Uri uri) @@ -43,7 +43,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new(parsingData.Representations.Full), previewUrl: new(parsingData.Representations.Thumb), - postUrl: new($"{BaseUrl}images/{parsingData.Id}"), + postUrl: new($"{PostBaseUrl}images/{parsingData.Id}"), sampleUri: new(parsingData.Representations.Large), rating: rating, tags: parsingData.Tags, diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 6b445a8..aeaa9b0 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -27,9 +27,9 @@ protected override Uri CreateQueryString(string query, string squery = "index") { if (query == "wiki") { - return new($"{BaseUrl}{query}"); + return new($"{APIBaseUrl}{query}"); } - return new($"{BaseUrl}{query}s"); + return new($"{APIBaseUrl}{query}s"); } protected override Task CreateRandomPostUriAsync(string[] tags) @@ -53,11 +53,7 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, previewUrl: parsingData.PreviewUrl != null ? new(parsingData.PreviewUrl) : null, - postUrl: new UriBuilder(BaseUrl) - { - Host = BaseUrl.Host.Replace("capi-v2", "beta"), - Path = $"/post/show/{parsingData.Id}", - }.Uri, + postUrl: new($"{PostBaseUrl}/post/show/{parsingData.Id}"), sampleUri: parsingData.SampleUrl != null && parsingData.SampleUrl.Contains("/preview/") ? new Uri(parsingData.SampleUrl) : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Select(x => x.NameEn), From ac9a9804bcfc87e867f99fae29b157f2333ea7f5 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 17:00:51 +0100 Subject: [PATCH 28/56] Fix more unit tests --- BooruSharp/Booru/Impl/Realbooru.cs | 7 ++++--- BooruSharp/Booru/Impl/Rule34.cs | 7 ++++--- BooruSharp/Booru/Template/Gelbooru02.cs | 12 +++++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/BooruSharp/Booru/Impl/Realbooru.cs b/BooruSharp/Booru/Impl/Realbooru.cs index 7984d8f..53ca7de 100644 --- a/BooruSharp/Booru/Impl/Realbooru.cs +++ b/BooruSharp/Booru/Impl/Realbooru.cs @@ -26,9 +26,9 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnails_{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, @@ -41,7 +41,7 @@ private protected override async Task GetPostSearchResultAsync creation: null, sources: null, score: parsingData.Score, - hash: null + hash: parsingData.Hash ); } @@ -56,6 +56,7 @@ public class SearchResult public int Height { init; get; } public int Width { init; get; } public int? Score { init; get; } + public string Hash { init; get; } } } } diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 7e14ddb..55251da 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -51,9 +51,9 @@ private protected override async Task GetPostSearchResultAsync return new PostSearchResult( fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}.jpg") : null, + sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, @@ -66,7 +66,7 @@ private protected override async Task GetPostSearchResultAsync creation: null, sources: null, score: parsingData.Score, - hash: null + hash: parsingData.Hash ); } @@ -81,6 +81,7 @@ public class SearchResult public int Height { init; get; } public int Width { init; get; } public int? Score { init; get; } + public string Hash { init; get; } } } } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 4759c9d..3867560 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -58,15 +58,20 @@ protected override void PreRequest(HttpRequestMessage message) } } + protected string GetUrlBody(string url) + { + return url.Split('.')[0]; + } + private protected override async Task GetPostSearchResultAsync(Uri uri) { var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image}") : null, + sampleUri: parsingData.Sample ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, @@ -79,7 +84,7 @@ private protected override async Task GetPostSearchResultAsync creation: null, sources: null, score: parsingData.Score, - hash: null + hash: parsingData.Hash ); } @@ -94,6 +99,7 @@ public class SearchResult public int Height { init; get; } public int Width { init; get; } public int? Score { init; get; } + public string Hash { init; get; } } protected async Task GetXmlAsync(string url) From 73f9bd6077792014413ecb9de1b9d5d054673593 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 18:04:42 +0100 Subject: [PATCH 29/56] Fix more unit tests --- BooruSharp/Booru/Impl/Realbooru.cs | 8 ++++---- BooruSharp/Booru/Impl/Rule34.cs | 10 ++++++---- BooruSharp/Booru/Impl/Xbooru.cs | 2 ++ BooruSharp/Booru/Template/Gelbooru02.cs | 9 ++------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/BooruSharp/Booru/Impl/Realbooru.cs b/BooruSharp/Booru/Impl/Realbooru.cs index 53ca7de..d4182c7 100644 --- a/BooruSharp/Booru/Impl/Realbooru.cs +++ b/BooruSharp/Booru/Impl/Realbooru.cs @@ -24,11 +24,11 @@ private protected override async Task GetPostSearchResultAsync { var parsingData = (await GetDataAsync(uri))[0]; - return new PostSearchResult( - fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), + return new PostSearchResult( // Somehow Realbooru must take the hash instead of directly using the image? + fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Hash}.{parsingData.Image.Split('.')[1]}"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Hash}.jpg"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}.jpg") : null, + sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Hash}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 55251da..6ea9936 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -50,10 +50,10 @@ private protected override async Task GetPostSearchResultAsync var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( - fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), + fileUrl: new($"{parsingData.FileUrl}"), + previewUrl: new($"{parsingData.PreviewUrl}"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}") : null, + sampleUri: parsingData.Sample == 1 ? new($"{parsingData.SampleUrl}") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, @@ -72,7 +72,9 @@ private protected override async Task GetPostSearchResultAsync public class SearchResult { - public string Directory { init; get; } + public string PreviewUrl { init; get; } + public string SampleUrl { init; get; } + public string FileUrl { init; get; } public string Image { init; get; } public int Id { init; get; } public int Sample { init; get; } diff --git a/BooruSharp/Booru/Impl/Xbooru.cs b/BooruSharp/Booru/Impl/Xbooru.cs index c28a2d9..5ea0f5e 100644 --- a/BooruSharp/Booru/Impl/Xbooru.cs +++ b/BooruSharp/Booru/Impl/Xbooru.cs @@ -18,6 +18,8 @@ public Xbooru() /// public override bool IsSafe => false; + public override Uri FileBaseUrl => new("https://img.xbooru.com"); public override Uri PreviewBaseUrl => new("https://img.xbooru.com"); + public override Uri SampleBaseUrl => new("https://img.xbooru.com"); } } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 3867560..98983fd 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -58,20 +58,15 @@ protected override void PreRequest(HttpRequestMessage message) } } - protected string GetUrlBody(string url) - { - return url.Split('.')[0]; - } - private protected override async Task GetPostSearchResultAsync(Uri uri) { var parsingData = (await GetDataAsync(uri))[0]; return new PostSearchResult( fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), - previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{GetUrlBody(parsingData.Image)}.jpg"), + previewUrl: new($"{PreviewBaseUrl}thumbnails/{parsingData.Directory}/thumbnail_{parsingData.Image.Split('.')[0]}.jpg"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{GetUrlBody(parsingData.Image)}.jpg") : null, + sampleUri: parsingData.Sample ? new($"{SampleBaseUrl}samples/{parsingData.Directory}/sample_{parsingData.Image.Split('.')[0]}.jpg") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, From 1bb610fe2a398ee814582d6197efdc652c5b111a Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 19:57:55 +0100 Subject: [PATCH 30/56] Improve unit tests --- BooruSharp.UnitTests/BooruTestData.cs | 3 +- BooruSharp.UnitTests/BooruTests.cs | 14 +++ BooruSharp.UnitTests/Utils.cs | 144 +++++++++++++++++++++--- BooruSharp/Booru/Template/Gelbooru02.cs | 9 +- 4 files changed, 147 insertions(+), 23 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTestData.cs b/BooruSharp.UnitTests/BooruTestData.cs index 09f0e9d..3d67a4c 100644 --- a/BooruSharp.UnitTests/BooruTestData.cs +++ b/BooruSharp.UnitTests/BooruTestData.cs @@ -4,6 +4,7 @@ namespace BooruSharp.UnitTests { public record BooruTestData { - public Type BooruType; + public required Type BooruType; + public required string[] Tags; } } diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 37bfca2..e078392 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,6 +1,7 @@ using BooruSharp.Search; using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Xunit; @@ -29,5 +30,18 @@ public async Task GetRandomImageAsync(BooruTestData data) await Assert.ThrowsAsync(async () => { await booru.GetRandomPostAsync(); }); } } + + [Theory] + [MemberData(nameof(BooruParams))] + public async Task GetRandomImageWith1TagAsync(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + var targetTag = data.Tags.Take(1).ToArray(); + var res = await booru.GetRandomPostAsync(targetTag); + var res2 = await booru.GetRandomPostAsync(targetTag); + await Utils.ValidatePostAsync(res, targetTag); + await Utils.ValidatePostAsync(res2, targetTag); + Assert.NotEqual(res.ID, res2.ID); + } } } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 126e8a9..b0af0ed 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -13,24 +13,132 @@ public static class Utils { public static IEnumerable BooruParams { get; } = new object[][] { - new object[] { new BooruTestData() { BooruType = typeof(Atfbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(DanbooruDonmai) } }, - new object[] { new BooruTestData() { BooruType = typeof(Derpibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(E621) } }, - new object[] { new BooruTestData() { BooruType = typeof(E926) } }, - new object[] { new BooruTestData() { BooruType = typeof(Furrybooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Gelbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Konachan) } }, - new object[] { new BooruTestData() { BooruType = typeof(Lolibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Ponybooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Realbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Rule34) } }, - new object[] { new BooruTestData() { BooruType = typeof(Safebooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Sakugabooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(SankakuComplex) } }, - new object[] { new BooruTestData() { BooruType = typeof(Twibooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Xbooru) } }, - new object[] { new BooruTestData() { BooruType = typeof(Yandere) } }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Atfbooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(DanbooruDonmai), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Derpibooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(E621), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(E926), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Furrybooru), + Tags = new[] { "kantai_collection", "clothing", "blush" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Gelbooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Konachan), + Tags = new[] { "kantai_collection", "blue_eyes", "hat" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Lolibooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Ponybooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Realbooru), + Tags = new[] { "kantai_collection", "asian", "swimsuit" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Rule34), + Tags = new[] { "kantai_collection", "asian", "swimsuit" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Safebooru), + Tags = new[] { "kantai_collection", "asian", "swimsuit" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Sakugabooru), + Tags = new[] { "kantai_collection", "explosions", "smoke" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(SankakuComplex), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Twibooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Xbooru), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, + new object[] { + new BooruTestData() + { + BooruType = typeof(Yandere), + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + } + }, //new BooruTestData() { BooruType = typeof(Pixiv) } }; diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 98983fd..08e1872 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Net.Http; +using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; @@ -27,7 +28,7 @@ protected Gelbooru02(string domain) protected override Uri CreateQueryString(string query, string squery = "index") { - return new($"{APIBaseUrl}index.php?page=dapi&s={query}&q=index&json=1"); + return new($"{APIBaseUrl}index.php?page=dapi&s={query}&q=index"); } protected override async Task CreateRandomPostUriAsync(string[] tags) @@ -37,16 +38,16 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) // We need to request /index.php?page=post&s=random and get the id given by the redirect HttpResponseMessage msg = await HttpClient.GetAsync($"{APIBaseUrl}index.php?page=post&s=random"); msg.EnsureSuccessStatusCode(); - return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id")); + return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id"), "json=1"); } - var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); + var url = CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "json=0"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) throw new Search.InvalidTags(); - return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); + return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max), "json=1"); } /// From 2e4a30fbd5b9f9d83219226f4f0ccf08d5a21bb7 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 20:31:08 +0100 Subject: [PATCH 31/56] Add more unit tests --- BooruSharp/Booru/Template/BooruOnRails.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index f0aa5ab..f1f1b48 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -34,7 +35,8 @@ protected override Task CreateRandomPostUriAsync(string[] tags) { return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=id.gte:0", "sf=random")); } - return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "sf=random")); + // Booru on rails use '+' as separator within a tag instead of '_' + return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString).Select(x => x.Replace('_', '+'))).ToLowerInvariant(), "sf=random")); } /// @@ -58,7 +60,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)).Posts[0]; + var posts = await GetDataAsync(uri); + if (!posts.Posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts.Posts[0]; Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; From aae1558023ad16f5e35c4be852181fd0546999da Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 20:39:30 +0100 Subject: [PATCH 32/56] Remove Furrybooru --- BooruSharp.UnitTests/Utils.cs | 11 ++--------- BooruSharp/Booru/Impl/Furrybooru.cs | 28 ---------------------------- 2 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 BooruSharp/Booru/Impl/Furrybooru.cs diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index b0af0ed..64a989b 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -48,13 +48,6 @@ public static class Utils Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } } }, - new object[] { - new BooruTestData() - { - BooruType = typeof(Furrybooru), - Tags = new[] { "kantai_collection", "clothing", "blush" } - } - }, new object[] { new BooruTestData() { @@ -203,8 +196,8 @@ public static async Task ValidatePostAsync(PostSearchResult res, string[] inputT if (res.Size != null) Assert.NotEqual(0, res.Size); foreach (var tag in inputTags) { - Assert.Contains(tag, res.Tags); - if (res.DetailedTags != null) Assert.Contains(tag, res.DetailedTags.Select(x => x.Name)); + Assert.Contains(tag.Replace('_', ' '), res.Tags.Select(x => x.Replace('_', ' '))); + if (res.DetailedTags != null) Assert.Contains(tag.Replace('_', ' '), res.DetailedTags.Select(x => x.Name.Replace('_', ' '))); } } } diff --git a/BooruSharp/Booru/Impl/Furrybooru.cs b/BooruSharp/Booru/Impl/Furrybooru.cs deleted file mode 100644 index 77c4a28..0000000 --- a/BooruSharp/Booru/Impl/Furrybooru.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using System.Web; -using System.Xml; -using System; - -namespace BooruSharp.Booru -{ - /// - /// FurryBooru. - /// https://furry.booru.org/ - /// - public sealed class Furrybooru : Template.Gelbooru02 - { - /// - /// Initializes a new instance of the class. - /// - public Furrybooru() - : base("furry.booru.org") - { } - - /// - public override bool IsSafe => false; - - public override bool CanSearchWithNoTag => false; - } -} From d2f963b5bf3338352ac0d7ce4727d69b9f0d5e65 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 20:42:15 +0100 Subject: [PATCH 33/56] Improve unit tests --- BooruSharp.UnitTests/Utils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 64a989b..7264298 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Xunit; @@ -177,7 +178,8 @@ private static async Task ValidateUrlAsync(string url) } var response = await Client.SendAsync(new HttpRequestMessage(HttpMethod.Head, url)); - return response.IsSuccessStatusCode; + return response.IsSuccessStatusCode + || response.StatusCode == HttpStatusCode.UnavailableForLegalReasons; // Takedown request on danbooru returns 451 code } public static async Task ValidatePostAsync(PostSearchResult res, string[] inputTags) From 31f331268d153cdc055348b993c99fa1e17acfa5 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 21:09:22 +0100 Subject: [PATCH 34/56] Add more unit tests --- BooruSharp.UnitTests/BooruTests.cs | 8 ++++++++ BooruSharp/Booru/Impl/Rule34.cs | 4 ++-- BooruSharp/Booru/Template/Danbooru.cs | 10 ++++++++-- BooruSharp/Booru/Template/E621.cs | 10 +++++++++- BooruSharp/Booru/Template/Gelbooru.cs | 10 ++++++++-- BooruSharp/Booru/Template/Gelbooru02.cs | 10 ++++++++-- BooruSharp/Booru/Template/Moebooru.cs | 10 ++++++++-- BooruSharp/Booru/Template/MyImouto.cs | 10 ++++++++-- BooruSharp/Booru/Template/Philomena.cs | 10 ++++++++-- BooruSharp/Booru/Template/Sankaku.cs | 10 ++++++++-- 10 files changed, 75 insertions(+), 17 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index e078392..5b707b8 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -43,5 +43,13 @@ public async Task GetRandomImageWith1TagAsync(BooruTestData data) await Utils.ValidatePostAsync(res2, targetTag); Assert.NotEqual(res.ID, res2.ID); } + + [Theory] + [MemberData(nameof(BooruParams))] + public async Task GetInvalidImageAsync(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + await Assert.ThrowsAsync(async () => { await booru.GetRandomPostAsync("azeazeazeazeaze"); }); + } } } diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 6ea9936..946e8bd 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -28,7 +28,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) { // We don't have to handle what happen when there is no tag because it'll throw before - var url = CreateUrl(new(_imageUrl.AbsoluteUri.Replace("index.json", "index.xml")), "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()); + var url = CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "json=0"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); @@ -39,7 +39,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) if (max > 20001) max = 20001; - return CreateUrl(_imageUrl, "limit=1", string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max)); + return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max), "json=1"); } /// diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index e714742..2870065 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; @@ -51,7 +52,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 2330821..6dc1f75 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -58,7 +58,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)).Posts[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: parsingData.File.Url != null ? new Uri(parsingData.File.Url) : null, @@ -69,12 +74,14 @@ private protected override async Task GetPostSearchResultAsync tags: parsingData.Tags.General .Concat(parsingData.Tags.Species) .Concat(parsingData.Tags.Character) + .Concat(parsingData.Tags.Copyright) .Concat(parsingData.Tags.Artist) .Concat(parsingData.Tags.Invalid) .Concat(parsingData.Tags.Lore) .Concat(parsingData.Tags.Meta), detailedTags: parsingData.Tags.Species.Select(x => new TagSearchResult(-1, x, TagType.Species, -1)) .Concat(parsingData.Tags.Character.Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) + .Concat(parsingData.Tags.Copyright.Select(x => new TagSearchResult(-1, x, TagType.Copyright, -1))) .Concat(parsingData.Tags.Artist.Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) .Concat(parsingData.Tags.Invalid.Select(x => new TagSearchResult(-1, x, TagType.Invalid, -1))) .Concat(parsingData.Tags.Lore.Select(x => new TagSearchResult(-1, x, TagType.Lore, -1))) @@ -124,6 +131,7 @@ public class Tags public string[] General { init; get; } public string[] Species { init; get; } public string[] Character { init; get; } + public string[] Copyright { init; get; } public string[] Artist { init; get; } public string[] Invalid { init; get; } public string[] Lore { init; get; } diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index be63a2d..0c7933a 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Globalization; using System.Linq; @@ -45,7 +46,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)).Post[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 08e1872..daa98b3 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -61,7 +62,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: new($"{FileBaseUrl}images/{parsingData.Directory}/{parsingData.Image}"), diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 6f92d8b..b5c1e71 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Net.Http; @@ -44,7 +45,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index 388adc7..8c6b74c 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Threading.Tasks; @@ -24,7 +25,12 @@ protected MyImouto(string domain) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 3fe6e15..4f7cd29 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Linq; using System.Threading.Tasks; @@ -32,7 +33,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri)).Images[0]; + var posts = await GetDataAsync(uri); + if (!posts.Images.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts.Images[0]; Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index aeaa9b0..83d359e 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; @@ -48,7 +49,12 @@ protected override void PreRequest(HttpRequestMessage message) // TODO: Doesn't private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var posts = await GetDataAsync(uri); + if (!posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts[0]; return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new(parsingData.FileUrl) : null, From 9b46f1c52118172c6dce47c4f5415ed017a3bcd5 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sat, 10 Dec 2022 21:15:18 +0100 Subject: [PATCH 35/56] Skip 503 errors in unit tests --- .../BooruSharp.UnitTests.csproj | 1 + BooruSharp.UnitTests/BooruTests.cs | 19 +++++++++++-------- BooruSharp.UnitTests/Utils.cs | 13 +++++++++++++ BooruSharp/Booru/Template/E621.cs | 3 ++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index f788c3e..029e115 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -17,6 +17,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 5b707b8..97a0cbf 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,4 +1,5 @@ using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Collections.Generic; using System.Linq; @@ -12,15 +13,15 @@ public class BooruTests : IClassFixture // Because of compile error public static IEnumerable BooruParams => Utils.BooruParams; - [Theory] + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetRandomImageAsync(BooruTestData data) { var booru = await Utils.GetAsync(data.BooruType); if (booru.CanSearchWithNoTag) { - var res = await booru.GetRandomPostAsync(); - var res2 = await booru.GetRandomPostAsync(); + var res = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(); }); + var res2 = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(); }); await Utils.ValidatePostAsync(res, Array.Empty()); await Utils.ValidatePostAsync(res2, Array.Empty()); Assert.NotEqual(res.ID, res2.ID); @@ -31,25 +32,27 @@ public async Task GetRandomImageAsync(BooruTestData data) } } - [Theory] + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetRandomImageWith1TagAsync(BooruTestData data) { var booru = await Utils.GetAsync(data.BooruType); var targetTag = data.Tags.Take(1).ToArray(); - var res = await booru.GetRandomPostAsync(targetTag); - var res2 = await booru.GetRandomPostAsync(targetTag); + var res = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(targetTag); }); + var res2 = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(targetTag); }); await Utils.ValidatePostAsync(res, targetTag); await Utils.ValidatePostAsync(res2, targetTag); Assert.NotEqual(res.ID, res2.ID); } - [Theory] + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetInvalidImageAsync(BooruTestData data) { var booru = await Utils.GetAsync(data.BooruType); - await Assert.ThrowsAsync(async () => { await booru.GetRandomPostAsync("azeazeazeazeaze"); }); + await Assert.ThrowsAsync(async () => { + await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync("azazazazaz"); }); + }); } } } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 7264298..6fcc7b4 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -182,6 +182,19 @@ private static async Task ValidateUrlAsync(string url) || response.StatusCode == HttpStatusCode.UnavailableForLegalReasons; // Takedown request on danbooru returns 451 code } + public static async Task DoWebRequest(Func> method) + { + try + { + return await method(); + } + catch (HttpRequestException hre) + { + Skip.If(hre.StatusCode == HttpStatusCode.ServiceUnavailable, "Service returned 503 error"); + throw; + } + } + public static async Task ValidatePostAsync(PostSearchResult res, string[] inputTags) { if (res.FileUrl != null) Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri}"); diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 6dc1f75..5c9fe3c 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -1,4 +1,5 @@ -using BooruSharp.Search.Post; +using BooruSharp.Search; +using BooruSharp.Search.Post; using BooruSharp.Search.Tag; using System; using System.Linq; From 81c5961d0f893136d2445f74312c6ddb7c8fa108 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Sun, 11 Dec 2022 19:14:10 +0100 Subject: [PATCH 36/56] Fix crash on random posts --- BooruSharp/Booru/Template/E621.cs | 6 +++--- BooruSharp/Booru/Template/Gelbooru.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 5c9fe3c..f89f7ec 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -59,12 +59,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var posts = await GetDataAsync(uri); - if (!posts.Any()) + var posts = await GetDataAsync(uri); + if (!posts.Posts.Any()) { throw new InvalidTags(); } - var parsingData = posts[0]; + var parsingData = posts.Posts[0]; return new PostSearchResult( fileUrl: parsingData.File.Url != null ? new Uri(parsingData.File.Url) : null, diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 0c7933a..17cd157 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -46,12 +46,12 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var posts = await GetDataAsync(uri); - if (!posts.Any()) + var posts = await GetDataAsync(uri); + if (!posts.Post.Any()) { throw new InvalidTags(); } - var parsingData = posts[0]; + var parsingData = posts.Post[0]; return new PostSearchResult( fileUrl: new(parsingData.FileUrl), From 3e48f5343fab6b9990ef41bde3295f249d0dd3b9 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Tue, 3 Jan 2023 16:40:01 +0100 Subject: [PATCH 37/56] Fix CI --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 333d9a6..0e18574 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,11 +14,11 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 - - name: Setup .NET Core + - uses: actions/checkout@v3 + - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 5.0.x + dotnet-version: 7.0.x - name: Initialize CodeQL uses: github/codeql-action/init@v1 - name: Install dependencies From 4df822a968811c01b9aa1d34a57d59a12abb86b5 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Tue, 3 Jan 2023 16:45:59 +0100 Subject: [PATCH 38/56] Comment Pixiv code --- BooruSharp.Others/Pixiv.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BooruSharp.Others/Pixiv.cs b/BooruSharp.Others/Pixiv.cs index 9db804b..748aa00 100644 --- a/BooruSharp.Others/Pixiv.cs +++ b/BooruSharp.Others/Pixiv.cs @@ -9,7 +9,7 @@ using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; - +/* namespace BooruSharp.Others { /// @@ -449,3 +449,4 @@ private static void AddUserAgentHeader(HttpRequestMessage request) private readonly string _hashSecret = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c"; } } +*/ \ No newline at end of file From 11b15d483563d6d290217db198ad42c9aa7e3f96 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 00:39:25 +0100 Subject: [PATCH 39/56] Fix Gelbooru crash --- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/BooruSharp.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 17cd157..723345b 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -47,7 +47,7 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); - if (!posts.Post.Any()) + if (posts.Post == null) { throw new InvalidTags(); } diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index 7880a1a..0adf4c9 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -3,7 +3,7 @@ net5.0 true - 4.0.0 + 4.0.0-beta Xwilarg BooruSharp is a C# library to browse Booru websites (Gelbooru, Konachan, etc...) easily From 2e731513223ad910d40daa7bb635506453846802 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 00:53:42 +0100 Subject: [PATCH 40/56] Fix CI and package settings --- BooruSharp.UnitTests/BooruSharp.UnitTests.csproj | 1 - BooruSharp/BooruSharp.csproj | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index 029e115..2bec90f 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -11,7 +11,6 @@ - all diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index 0adf4c9..b8d09df 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -15,6 +15,7 @@ Booru Gelbooru Image C-Sharp Atfbooru DanbooruDonmai Danbooru E621 E926 FurryBooru Konachan Lolibooru Realbooru Rule34 SankakuComplex Safebooru Sakugabooru Xbooru Yandere .\xmldoc\$(TargetFramework)\BooruSharp.xml MIT + README.md @@ -24,6 +25,10 @@ + + True + \ + True From feecee37844a52d3f8cc0bd446d3d2953113f17f Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 01:43:32 +0100 Subject: [PATCH 41/56] Add max number of tags --- .../BooruSharp.UnitTests.csproj | 4 ++++ BooruSharp.UnitTests/BooruTests.cs | 18 +++++++++++++++++- BooruSharp.UnitTests/Utils.cs | 18 +++++++++--------- BooruSharp/Booru/ABooru.cs | 4 +--- BooruSharp/Booru/Impl/DanbooruDonmai.cs | 6 ++---- BooruSharp/Booru/Template/E621.cs | 7 ++----- BooruSharp/Search/Post/ABooru.cs | 4 ++++ BooruSharp/Search/TooManyTags.cs | 13 ++----------- 8 files changed, 41 insertions(+), 33 deletions(-) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index 2bec90f..9d4ac6d 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -11,6 +11,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 97a0cbf..e835e53 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,5 +1,4 @@ using BooruSharp.Search; -using BooruSharp.Search.Post; using System; using System.Collections.Generic; using System.Linq; @@ -45,6 +44,23 @@ public async Task GetRandomImageWith1TagAsync(BooruTestData data) Assert.NotEqual(res.ID, res2.ID); } + [SkippableTheory] + [MemberData(nameof(BooruParams))] + public async Task GetRandomImageWith3TagsAsync(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + var targetTag = data.Tags.Take(3).ToArray(); + if (booru.MaxNumberOfTags == -1) + { + var res = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(targetTag); }); + await Utils.ValidatePostAsync(res, targetTag); + } + else + { + await Assert.ThrowsAsync(async () => { await booru.GetRandomPostAsync(targetTag); }); + } + } + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetInvalidImageAsync(BooruTestData data) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 6fcc7b4..be65732 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -32,21 +32,21 @@ public static class Utils new BooruTestData() { BooruType = typeof(Derpibooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" } } }, new object[] { new BooruTestData() { BooruType = typeof(E621), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" } } }, new object[] { new BooruTestData() { BooruType = typeof(E926), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" } } }, new object[] { @@ -74,7 +74,7 @@ public static class Utils new BooruTestData() { BooruType = typeof(Ponybooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" } } }, new object[] { @@ -88,14 +88,14 @@ public static class Utils new BooruTestData() { BooruType = typeof(Rule34), - Tags = new[] { "kantai_collection", "asian", "swimsuit" } + Tags = new[] { "kantai_collection", "outdoors", "swimsuit" } } }, new object[] { new BooruTestData() { BooruType = typeof(Safebooru), - Tags = new[] { "kantai_collection", "asian", "swimsuit" } + Tags = new[] { "kantai_collection", "outdoors", "swimsuit" } } }, new object[] { @@ -116,21 +116,21 @@ public static class Utils new BooruTestData() { BooruType = typeof(Twibooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" } } }, new object[] { new BooruTestData() { BooruType = typeof(Xbooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" } } }, new object[] { new BooruTestData() { BooruType = typeof(Yandere), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "sweater", "bra" } } }, //new BooruTestData() { BooruType = typeof(Pixiv) } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 7ac8093..c8e72f4 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -14,6 +14,7 @@ namespace BooruSharp.Booru public abstract partial class ABooru { public virtual bool CanSearchWithNoTag => true; + public virtual int MaxNumberOfTags => -1; public virtual Uri FileBaseUrl => APIBaseUrl; public virtual Uri PreviewBaseUrl => APIBaseUrl; @@ -126,9 +127,6 @@ private protected async Task GetJsonAsync(string url) if (msg.StatusCode == HttpStatusCode.Forbidden) throw new AuthentificationRequired(); - if (msg.StatusCode == (HttpStatusCode)422) - throw new TooManyTags(); - msg.EnsureSuccessStatusCode(); return await msg.Content.ReadAsStringAsync(); diff --git a/BooruSharp/Booru/Impl/DanbooruDonmai.cs b/BooruSharp/Booru/Impl/DanbooruDonmai.cs index d7be217..7ec1a13 100644 --- a/BooruSharp/Booru/Impl/DanbooruDonmai.cs +++ b/BooruSharp/Booru/Impl/DanbooruDonmai.cs @@ -19,14 +19,12 @@ public DanbooruDonmai() protected override Task CreateRandomPostUriAsync(string[] tags) { - if (tags.Length > 2) - { - throw new Search.TooManyTags(); - } return base.CreateRandomPostUriAsync(tags); } /// public override bool IsSafe => false; + /// + public override int MaxNumberOfTags => 2; } } diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index f89f7ec..38d5d3c 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -40,10 +40,6 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - if (tags.Length > 2) - { - throw new Search.TooManyTags(); - } return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); } @@ -80,7 +76,8 @@ private protected override async Task GetPostSearchResultAsync .Concat(parsingData.Tags.Invalid) .Concat(parsingData.Tags.Lore) .Concat(parsingData.Tags.Meta), - detailedTags: parsingData.Tags.Species.Select(x => new TagSearchResult(-1, x, TagType.Species, -1)) + detailedTags: parsingData.Tags.General.Select(x => new TagSearchResult(-1, x, TagType.Trivia, -1)) + .Concat(parsingData.Tags.Species.Select(x => new TagSearchResult(-1, x, TagType.Species, -1))) .Concat(parsingData.Tags.Character.Select(x => new TagSearchResult(-1, x, TagType.Character, -1))) .Concat(parsingData.Tags.Copyright.Select(x => new TagSearchResult(-1, x, TagType.Copyright, -1))) .Concat(parsingData.Tags.Artist.Select(x => new TagSearchResult(-1, x, TagType.Artist, -1))) diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 3a3f978..a042d95 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -34,6 +34,10 @@ public async Task GetRandomPostAsync(params string[] tagsArg) { throw new FeatureUnavailable("This booru doesn't support search with no tag"); } + if (MaxNumberOfTags != -1 && tags.Length > MaxNumberOfTags) + { + throw new TooManyTags(MaxNumberOfTags); + } var url = await CreateRandomPostUriAsync(tags); return await GetPostSearchResultAsync(url); diff --git a/BooruSharp/Search/TooManyTags.cs b/BooruSharp/Search/TooManyTags.cs index a3c8684..c6d3a8a 100644 --- a/BooruSharp/Search/TooManyTags.cs +++ b/BooruSharp/Search/TooManyTags.cs @@ -13,17 +13,8 @@ public class TooManyTags : ArgumentException /// /// Initializes a new instance of the class. /// - public TooManyTags() - : base("You can't have more than 2 tags for a search with this booru.") - { } - - /// - /// Initializes a new instance of the - /// class with a specified error message. - /// - /// The error message that explains the reason for the exception. - public TooManyTags(string message) - : base(message) + public TooManyTags(int maxTagCount) + : base($"You can't have more than {maxTagCount} tags for a search with this booru.") { } /// From e613e1fb56bb26e89f72d56650eebfab4bb93b22 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 16:49:00 +0100 Subject: [PATCH 42/56] Implement tags by ID --- BooruSharp.UnitTests/BooruTestData.cs | 1 + BooruSharp.UnitTests/BooruTests.cs | 16 +++++++ BooruSharp.UnitTests/Utils.cs | 51 +++++++++++++++-------- BooruSharp/Booru/ABooru.cs | 14 +------ BooruSharp/Booru/Impl/Lolibooru.cs | 2 + BooruSharp/Booru/Impl/Rule34.cs | 4 +- BooruSharp/Booru/Impl/Sakugabooru.cs | 4 +- BooruSharp/Booru/Impl/SankakuComplex.cs | 4 +- BooruSharp/Booru/Template/BooruOnRails.cs | 9 +++- BooruSharp/Booru/Template/Danbooru.cs | 7 +++- BooruSharp/Booru/Template/E621.cs | 7 +++- BooruSharp/Booru/Template/Gelbooru.cs | 7 +++- BooruSharp/Booru/Template/Gelbooru02.cs | 11 +++-- BooruSharp/Booru/Template/Moebooru.cs | 7 +++- BooruSharp/Booru/Template/Philomena.cs | 5 +++ BooruSharp/Booru/Template/Sankaku.cs | 7 +++- BooruSharp/Search/Post/ABooru.cs | 35 ++++++++-------- 17 files changed, 130 insertions(+), 61 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTestData.cs b/BooruSharp.UnitTests/BooruTestData.cs index 3d67a4c..4026872 100644 --- a/BooruSharp.UnitTests/BooruTestData.cs +++ b/BooruSharp.UnitTests/BooruTestData.cs @@ -6,5 +6,6 @@ public record BooruTestData { public required Type BooruType; public required string[] Tags; + public required int ValidPostId; } } diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index e835e53..4e1f086 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -70,5 +70,21 @@ await Assert.ThrowsAsync(async () => { await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync("azazazazaz"); }); }); } + + [SkippableTheory] + [MemberData(nameof(BooruParams))] + public async Task GetImageByIdAsync(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + if (booru.HasPostByIdAPI) + { + var res = await Utils.DoWebRequest(async () => { return await booru.GetPostByIdAsync(data.ValidPostId); }); + await Utils.ValidatePostAsync(res, Array.Empty()); + } + else + { + await Assert.ThrowsAsync(async () => { await booru.GetPostByIdAsync(data.ValidPostId); }); + } + } } } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index be65732..39afaab 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -18,119 +18,136 @@ public static class Utils new BooruTestData() { BooruType = typeof(Atfbooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, + ValidPostId = 419308 } }, new object[] { new BooruTestData() { BooruType = typeof(DanbooruDonmai), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, + ValidPostId = 5972904 } }, new object[] { new BooruTestData() { BooruType = typeof(Derpibooru), - Tags = new[] { "pegasus", "raised_tail", "lying_down" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" }, + ValidPostId = 3021165 } }, new object[] { new BooruTestData() { BooruType = typeof(E621), - Tags = new[] { "kantai_collection", "blue_eyes", "outside" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, + ValidPostId = 1329650 } }, new object[] { new BooruTestData() { BooruType = typeof(E926), - Tags = new[] { "kantai_collection", "blue_eyes", "outside" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, + ValidPostId = 1329650 } }, new object[] { new BooruTestData() { BooruType = typeof(Gelbooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, + ValidPostId = 8113595 } }, new object[] { new BooruTestData() { BooruType = typeof(Konachan), - Tags = new[] { "kantai_collection", "blue_eyes", "hat" } + Tags = new[] { "kantai_collection", "blue_eyes", "hat" }, + ValidPostId = 351127 } }, new object[] { new BooruTestData() { BooruType = typeof(Lolibooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, + ValidPostId = 516532 } }, new object[] { new BooruTestData() { BooruType = typeof(Ponybooru), - Tags = new[] { "pegasus", "raised_tail", "lying_down" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" }, + ValidPostId = 3262026 } }, new object[] { new BooruTestData() { BooruType = typeof(Realbooru), - Tags = new[] { "kantai_collection", "asian", "swimsuit" } + Tags = new[] { "kantai_collection", "asian", "swimsuit" }, + ValidPostId = 809254 } }, new object[] { new BooruTestData() { BooruType = typeof(Rule34), - Tags = new[] { "kantai_collection", "outdoors", "swimsuit" } + Tags = new[] { "kantai_collection", "outdoors", "swimsuit" }, + ValidPostId = 139056 } }, new object[] { new BooruTestData() { BooruType = typeof(Safebooru), - Tags = new[] { "kantai_collection", "outdoors", "swimsuit" } + Tags = new[] { "kantai_collection", "outdoors", "swimsuit" }, + ValidPostId = 4248721 } }, new object[] { new BooruTestData() { BooruType = typeof(Sakugabooru), - Tags = new[] { "kantai_collection", "explosions", "smoke" } + Tags = new[] { "kantai_collection", "explosions", "smoke" }, + ValidPostId = 157179 } }, new object[] { new BooruTestData() { BooruType = typeof(SankakuComplex), - Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" } + Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, + ValidPostId = 32458685 } }, new object[] { new BooruTestData() { BooruType = typeof(Twibooru), - Tags = new[] { "pegasus", "raised_tail", "lying_down" } + Tags = new[] { "pegasus", "raised_tail", "lying_down" }, + ValidPostId = 2869409 } }, new object[] { new BooruTestData() { BooruType = typeof(Xbooru), - Tags = new[] { "kantai_collection", "blue_eyes", "outside" } + Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, + ValidPostId = 830871 } }, new object[] { new BooruTestData() { BooruType = typeof(Yandere), - Tags = new[] { "kantai_collection", "sweater", "bra" } + Tags = new[] { "kantai_collection", "sweater", "bra" }, + ValidPostId = 1052829 } }, //new BooruTestData() { BooruType = typeof(Pixiv) } diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index c8e72f4..e19d563 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -14,6 +14,7 @@ namespace BooruSharp.Booru public abstract partial class ABooru { public virtual bool CanSearchWithNoTag => true; + public virtual bool HasPostByIdAPI => true; public virtual int MaxNumberOfTags => -1; public virtual Uri FileBaseUrl => APIBaseUrl; @@ -64,6 +65,7 @@ protected virtual void PreRequest(HttpRequestMessage message) /// /// List of tags sent by the user protected abstract Task CreateRandomPostUriAsync(string[] tags); + protected abstract Task CreatePostByIdUriAsync(int id); protected virtual async Task GetDataArray(Uri url) { @@ -132,18 +134,6 @@ private protected async Task GetJsonAsync(string url) return await msg.Content.ReadAsStringAsync(); } - protected Uri CreateUrl(Uri url, params string[] args) - { - var builder = new UriBuilder(url); - - if (builder.Query?.Length > 1) - builder.Query = builder.Query[1..] + "&" + string.Join("&", args); - else - builder.Query = string.Join("&", args); - - return builder.Uri; - } - /* private string SearchArg(string value) { diff --git a/BooruSharp/Booru/Impl/Lolibooru.cs b/BooruSharp/Booru/Impl/Lolibooru.cs index 743d4e8..7505a08 100644 --- a/BooruSharp/Booru/Impl/Lolibooru.cs +++ b/BooruSharp/Booru/Impl/Lolibooru.cs @@ -15,6 +15,8 @@ public Lolibooru() /// public override bool IsSafe => false; + /// + public override bool HasPostByIdAPI => false; /* diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 946e8bd..6a33b02 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -28,7 +28,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) { // We don't have to handle what happen when there is no tag because it'll throw before - var url = CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "json=0"); + var url = new Uri($"{_imageUrl}&limit=1&id={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=1"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); @@ -39,7 +39,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) if (max > 20001) max = 20001; - return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max), "json=1"); + return new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&pid={Random.Next(0, max)}&json=1"); } /// diff --git a/BooruSharp/Booru/Impl/Sakugabooru.cs b/BooruSharp/Booru/Impl/Sakugabooru.cs index 41e9e51..9de0b84 100644 --- a/BooruSharp/Booru/Impl/Sakugabooru.cs +++ b/BooruSharp/Booru/Impl/Sakugabooru.cs @@ -10,10 +10,12 @@ public sealed class Sakugabooru : Template.Moebooru /// Initializes a new instance of the class. /// public Sakugabooru() - : base("sakugabooru.com")//TODO:, BooruOptions.NoLastComments) + : base("sakugabooru.com") { } /// public override bool IsSafe => false; + /// + public override bool HasPostByIdAPI => false; } } diff --git a/BooruSharp/Booru/Impl/SankakuComplex.cs b/BooruSharp/Booru/Impl/SankakuComplex.cs index db31745..01fad58 100644 --- a/BooruSharp/Booru/Impl/SankakuComplex.cs +++ b/BooruSharp/Booru/Impl/SankakuComplex.cs @@ -17,7 +17,9 @@ public SankakuComplex() /// public override bool IsSafe => false; - + /// + public override bool HasPostByIdAPI => false; + /// public override Uri PostBaseUrl => new("https://beta.sankakucomplex.com"); } } diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index f1f1b48..c8dc2fd 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -33,10 +33,15 @@ protected override Task CreateRandomPostUriAsync(string[] tags) { if (!tags.Any()) { - return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=id.gte:0", "sf=random")); + return Task.FromResult(new Uri($"{_imageUrl}?per_page=1&q=id.gte:0&sf=random")); } // Booru on rails use '+' as separator within a tag instead of '_' - return Task.FromResult(CreateUrl(_imageUrl, "per_page=1", "q=" + string.Join(",", tags.Select(Uri.EscapeDataString).Select(x => x.Replace('_', '+'))).ToLowerInvariant(), "sf=random")); + return Task.FromResult(new Uri($"{_imageUrl}?per_page=1&q={string.Join(",", tags.Select(Uri.EscapeDataString).Select(x => x.Replace('_', '+'))).ToLowerInvariant()}&sf=random")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}/{id}")); } /// diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 2870065..fd2e6bb 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -38,7 +38,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "random=true")); + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&random=true")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}/{id}.json")); } /// diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 38d5d3c..60fc84a 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -40,7 +40,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}+order:random")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&id={id}")); } /// diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index 723345b..ec9798a 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -32,7 +32,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+sort:random")); + return Task.FromResult(new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}+sort:random")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&id={id}")); } /// diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index daa98b3..6477969 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -32,6 +32,11 @@ protected override Uri CreateQueryString(string query, string squery = "index") return new($"{APIBaseUrl}index.php?page=dapi&s={query}&q=index"); } + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}&limit=1&id={id}")); + } + protected override async Task CreateRandomPostUriAsync(string[] tags) { if (!tags.Any()) @@ -39,16 +44,16 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) // We need to request /index.php?page=post&s=random and get the id given by the redirect HttpResponseMessage msg = await HttpClient.GetAsync($"{APIBaseUrl}index.php?page=post&s=random"); msg.EnsureSuccessStatusCode(); - return CreateUrl(_imageUrl, "limit=1", "id=" + HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id"), "json=1"); + return new Uri($"{_imageUrl}&limit=1&id={HttpUtility.ParseQueryString(msg.RequestMessage.RequestUri.Query).Get("id")}&json=1"); } - var url = CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "json=0"); + var url = new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=0"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) throw new Search.InvalidTags(); - return CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant(), "pid=" + Random.Next(0, max), "json=1"); + return new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&pid={Random.Next(0, max)}&json=1"); } /// diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index b5c1e71..7bbe13f 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -31,7 +31,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}+order:random")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}?tags=id:" + id)); } /// diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 4f7cd29..9203e6a 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -31,6 +31,11 @@ protected override Uri CreateQueryString(string query, string squery = "index") return new($"{APIBaseUrl}api/v1/json/search/{query}s"); } + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}/{id}")); + } + private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 83d359e..475a2b9 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -35,7 +35,12 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(CreateUrl(_imageUrl, "limit=1", "tags=" + string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant() + "+order:random")); + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}+order:random")); + } + + protected override Task CreatePostByIdUriAsync(int id) + { + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&id={id}")); } /// diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index a042d95..d502890 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using static BooruSharp.Booru.Template.E621; namespace BooruSharp.Booru { @@ -42,6 +43,22 @@ public async Task GetRandomPostAsync(params string[] tagsArg) var url = await CreateRandomPostUriAsync(tags); return await GetPostSearchResultAsync(url); } + + /// + /// Searches for a post using its ID. + /// + /// The ID of the post to search. + /// The task object representing the asynchronous operation. + /// + /// + public virtual async Task GetPostByIdAsync(int id) + { + if (!HasPostByIdAPI) + throw new FeatureUnavailable(); + + var url = await CreatePostByIdUriAsync(id); + return await GetPostSearchResultAsync(url); + } /* private const int _limitedTagsSearchCount = 2; private const int _increasedPostLimitCount = 20001; @@ -68,24 +85,6 @@ private string GetLimit(int quantity) return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), "md5=" + md5)); } - /// - /// Searches for a post using its ID. - /// - /// The ID of the post to search. - /// The task object representing the asynchronous operation. - /// - /// - public virtual async Task GetPostByIdAsync(int id) - { - if (!HasPostByIdAPI) - throw new Search.FeatureUnavailable(); - - if (_format == UrlFormat.Danbooru) return await GetSearchResultFromUrlAsync(BaseUrl + "posts/" + id + ".json"); - if (_format == UrlFormat.Philomena) return await GetSearchResultFromUrlAsync($"{BaseUrl}api/v1/json/images/{id}"); - if (_format == UrlFormat.BooruOnRails) return await GetSearchResultFromUrlAsync($"{BaseUrl}api/v3/posts/{id}"); - if (_format == UrlFormat.PostIndexJson) return await GetSearchResultFromUrlAsync(_imageUrl + "?tags=id:" + id); - return await GetSearchResultFromUrlAsync(CreateUrl(_imageUrl, GetLimit(1), "id=" + id)); - } /// /// Gets the total number of available posts. If array is specified From 964fcee65613fd0064c9b85bfc440f0dce483004 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 17:25:56 +0100 Subject: [PATCH 43/56] Fix unit tests for rating check --- .../BooruSharp.UnitTests.csproj | 1 + BooruSharp.UnitTests/BooruTestData.cs | 1 + BooruSharp.UnitTests/BooruTests.cs | 14 +++++ BooruSharp.UnitTests/Utils.cs | 58 +++++++++++++------ 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj index 9d4ac6d..dfd294e 100644 --- a/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj +++ b/BooruSharp.UnitTests/BooruSharp.UnitTests.csproj @@ -15,6 +15,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/BooruSharp.UnitTests/BooruTestData.cs b/BooruSharp.UnitTests/BooruTestData.cs index 4026872..ac9c2e0 100644 --- a/BooruSharp.UnitTests/BooruTestData.cs +++ b/BooruSharp.UnitTests/BooruTestData.cs @@ -7,5 +7,6 @@ public record BooruTestData public required Type BooruType; public required string[] Tags; public required int ValidPostId; + public required string ExplicitTag; } } diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 4e1f086..29c3a14 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -1,4 +1,5 @@ using BooruSharp.Search; +using BooruSharp.Search.Post; using System; using System.Collections.Generic; using System.Linq; @@ -71,6 +72,19 @@ await Assert.ThrowsAsync(async () => { }); } + [SkippableTheory] + [MemberData(nameof(BooruParams))] + public async Task GetExplicitImageAsync(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + Assert.True((booru.IsSafe && data.ExplicitTag == null) || (!booru.IsSafe && data.ExplicitTag != null)); + if (!booru.IsSafe) + { + var post = await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync(data.ExplicitTag); }); + Assert.Equal(Rating.Explicit, post.Rating); + } + } + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetImageByIdAsync(BooruTestData data) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 39afaab..7e0084e 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Net.Http; +using System.Net.Sockets; using System.Threading.Tasks; using Xunit; @@ -19,7 +20,8 @@ public static class Utils { BooruType = typeof(Atfbooru), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, - ValidPostId = 419308 + ValidPostId = 419308, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -27,7 +29,8 @@ public static class Utils { BooruType = typeof(DanbooruDonmai), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, - ValidPostId = 5972904 + ValidPostId = 5972904, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -35,7 +38,8 @@ public static class Utils { BooruType = typeof(Derpibooru), Tags = new[] { "pegasus", "raised_tail", "lying_down" }, - ValidPostId = 3021165 + ValidPostId = 3021165, + ExplicitTag = "spread+pussy" } }, new object[] { @@ -43,7 +47,8 @@ public static class Utils { BooruType = typeof(E621), Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, - ValidPostId = 1329650 + ValidPostId = 1329650, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -51,7 +56,8 @@ public static class Utils { BooruType = typeof(E926), Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, - ValidPostId = 1329650 + ValidPostId = 1329650, + ExplicitTag = null } }, new object[] { @@ -59,7 +65,8 @@ public static class Utils { BooruType = typeof(Gelbooru), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, - ValidPostId = 8113595 + ValidPostId = 8113595, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -67,7 +74,8 @@ public static class Utils { BooruType = typeof(Konachan), Tags = new[] { "kantai_collection", "blue_eyes", "hat" }, - ValidPostId = 351127 + ValidPostId = 351127, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -75,7 +83,8 @@ public static class Utils { BooruType = typeof(Lolibooru), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, - ValidPostId = 516532 + ValidPostId = 516532, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -83,7 +92,8 @@ public static class Utils { BooruType = typeof(Ponybooru), Tags = new[] { "pegasus", "raised_tail", "lying_down" }, - ValidPostId = 3262026 + ValidPostId = 3262026, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -91,7 +101,8 @@ public static class Utils { BooruType = typeof(Realbooru), Tags = new[] { "kantai_collection", "asian", "swimsuit" }, - ValidPostId = 809254 + ValidPostId = 809254, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -99,7 +110,8 @@ public static class Utils { BooruType = typeof(Rule34), Tags = new[] { "kantai_collection", "outdoors", "swimsuit" }, - ValidPostId = 139056 + ValidPostId = 139056, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -107,7 +119,8 @@ public static class Utils { BooruType = typeof(Safebooru), Tags = new[] { "kantai_collection", "outdoors", "swimsuit" }, - ValidPostId = 4248721 + ValidPostId = 4248721, + ExplicitTag = null } }, new object[] { @@ -115,7 +128,8 @@ public static class Utils { BooruType = typeof(Sakugabooru), Tags = new[] { "kantai_collection", "explosions", "smoke" }, - ValidPostId = 157179 + ValidPostId = 157179, + ExplicitTag = "rating:explicit" // Looks like there isn't any tag that always return explicit stuffs } }, new object[] { @@ -123,7 +137,8 @@ public static class Utils { BooruType = typeof(SankakuComplex), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, - ValidPostId = 32458685 + ValidPostId = 32458685, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -131,7 +146,8 @@ public static class Utils { BooruType = typeof(Twibooru), Tags = new[] { "pegasus", "raised_tail", "lying_down" }, - ValidPostId = 2869409 + ValidPostId = 2869409, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -139,7 +155,8 @@ public static class Utils { BooruType = typeof(Xbooru), Tags = new[] { "kantai_collection", "blue_eyes", "outside" }, - ValidPostId = 830871 + ValidPostId = 830871, + ExplicitTag = "spread_pussy" } }, new object[] { @@ -147,7 +164,8 @@ public static class Utils { BooruType = typeof(Yandere), Tags = new[] { "kantai_collection", "sweater", "bra" }, - ValidPostId = 1052829 + ValidPostId = 1052829, + ExplicitTag = "pussy" } }, //new BooruTestData() { BooruType = typeof(Pixiv) } @@ -208,6 +226,10 @@ public static async Task DoWebRequest(Func> method) catch (HttpRequestException hre) { Skip.If(hre.StatusCode == HttpStatusCode.ServiceUnavailable, "Service returned 503 error"); + if (Environment.GetEnvironmentVariable("CI") != "true") + { + Skip.If(hre.InnerException is SocketException); + } throw; } } @@ -218,7 +240,7 @@ public static async Task ValidatePostAsync(PostSearchResult res, string[] inputT if (res.PreviewUrl != null) Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri}"); if (res.PostUrl != null) Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri}"); if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri}"); - Assert.InRange(res.Rating, Rating.General, Rating.Explicit); + Assert.InRange(res.Rating, (Rating)(-1), Rating.Explicit); Assert.NotEmpty(res.Tags); Assert.NotEqual(0, res.ID); Assert.NotEqual(0, res.Width); From 99725ea5bd3e6432a5d3de298cc145365c55bcb2 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 11 Jan 2023 23:20:20 +0100 Subject: [PATCH 44/56] Fix unit tests --- BooruSharp.UnitTests/Utils.cs | 4 ++-- BooruSharp/Booru/ABooru.cs | 3 --- BooruSharp/Booru/Template/Danbooru.cs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 7e0084e..1a7b239 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -39,7 +39,7 @@ public static class Utils BooruType = typeof(Derpibooru), Tags = new[] { "pegasus", "raised_tail", "lying_down" }, ValidPostId = 3021165, - ExplicitTag = "spread+pussy" + ExplicitTag = "spread_pussy" } }, new object[] { @@ -138,7 +138,7 @@ public static class Utils BooruType = typeof(SankakuComplex), Tags = new[] { "kantai_collection", "blue_eyes", "outdoors" }, ValidPostId = 32458685, - ExplicitTag = "spread_pussy" + ExplicitTag = "spread_vagina" } }, new object[] { diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index e19d563..9d5c528 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -126,9 +126,6 @@ private protected async Task GetJsonAsync(string url) PreRequest(message); var msg = await HttpClient.SendAsync(message); - if (msg.StatusCode == HttpStatusCode.Forbidden) - throw new AuthentificationRequired(); - msg.EnsureSuccessStatusCode(); return await msg.Content.ReadAsStringAsync(); diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index fd2e6bb..f3ddd64 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -38,7 +38,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreateRandomPostUriAsync(string[] tags) { - return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&random=true")); + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}+random:1")); } protected override Task CreatePostByIdUriAsync(int id) From 15190271f7c7cff3e90e234bb627bba798520a24 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 18:10:51 +0200 Subject: [PATCH 45/56] Fix more unit tests --- BooruSharp.UnitTests/Utils.cs | 1 + BooruSharp/Booru/Impl/Rule34.cs | 6 ++-- BooruSharp/Booru/Impl/Twibooru.cs | 41 +++++++++++++++++++++++++ BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 1a7b239..bf66d49 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -225,6 +225,7 @@ public static async Task DoWebRequest(Func> method) } catch (HttpRequestException hre) { + Skip.If(hre.StatusCode == HttpStatusCode.BadGateway, "Service returned 502 error"); Skip.If(hre.StatusCode == HttpStatusCode.ServiceUnavailable, "Service returned 503 error"); if (Environment.GetEnvironmentVariable("CI") != "true") { diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 6a33b02..eda8639 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -28,7 +28,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) { // We don't have to handle what happen when there is no tag because it'll throw before - var url = new Uri($"{_imageUrl}&limit=1&id={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=1"); + var url = new Uri($"{_imageUrl}&limit=1&id={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=0"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); @@ -53,7 +53,7 @@ private protected override async Task GetPostSearchResultAsync fileUrl: new($"{parsingData.FileUrl}"), previewUrl: new($"{parsingData.PreviewUrl}"), postUrl: new($"{PostBaseUrl}index.php?page=post&s=view&id={parsingData.Id}"), - sampleUri: parsingData.Sample == 1 ? new($"{parsingData.SampleUrl}") : null, + sampleUri: parsingData.Sample ? new($"{parsingData.SampleUrl}") : null, rating: GetRating(parsingData.Rating[0]), tags: parsingData.Tags.Split(), detailedTags: null, @@ -77,7 +77,7 @@ public class SearchResult public string FileUrl { init; get; } public string Image { init; get; } public int Id { init; get; } - public int Sample { init; get; } + public bool Sample { init; get; } public string Rating { init; get; } public string Tags { init; get; } public int Height { init; get; } diff --git a/BooruSharp/Booru/Impl/Twibooru.cs b/BooruSharp/Booru/Impl/Twibooru.cs index 517f60b..2e5592d 100644 --- a/BooruSharp/Booru/Impl/Twibooru.cs +++ b/BooruSharp/Booru/Impl/Twibooru.cs @@ -1,4 +1,9 @@ using BooruSharp.Booru.Template; +using BooruSharp.Search.Post; +using BooruSharp.Search; +using System.Threading.Tasks; +using System; +using System.Linq; namespace BooruSharp.Booru { @@ -19,5 +24,41 @@ public Twibooru() : base("twibooru.org") /// protected override int FilterID => 2; + + private protected override async Task GetPostSearchResultAsync(Uri uri) + { + var posts = await GetDataAsync(uri); + if (!posts.Posts.Any()) + { + throw new InvalidTags(); + } + var parsingData = posts.Posts[0]; + + Rating rating; + if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; + else if (parsingData.Tags.Contains("questionable")) rating = Rating.Questionable; + else if (parsingData.Tags.Contains("suggestive")) rating = Rating.Safe; + else if (parsingData.Tags.Contains("safe")) rating = Rating.General; + else rating = (Rating)(-1); // Some images doesn't have a rating + return new PostSearchResult( + fileUrl: new(parsingData.Representations.Full), + previewUrl: new(parsingData.Representations.Thumb), + postUrl: new($"{PostBaseUrl}{parsingData.Id}"), + sampleUri: new(parsingData.Representations.Large), + rating: rating, + tags: parsingData.Tags, + detailedTags: null, + id: parsingData.Id, + size: parsingData.Size, + height: parsingData.Height, + width: parsingData.Width, + previewHeight: null, + previewWidth: null, + creation: parsingData.CreatedAt, + sources: string.IsNullOrEmpty(parsingData.SourceUrl) ? Array.Empty() : new[] { parsingData.SourceUrl }, + score: parsingData.Score, + hash: parsingData.Sha512Hash + ); + } } } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index 6477969..c11453e 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -51,7 +51,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) - throw new Search.InvalidTags(); + throw new InvalidTags(); return new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&pid={Random.Next(0, max)}&json=1"); } From cd614a9356ecb60e440f0d2e98b47034c5c5411b Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 18:29:29 +0200 Subject: [PATCH 46/56] Fix crash --- BooruSharp/Booru/Impl/Rule34.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index eda8639..072130c 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -47,7 +47,13 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var parsingData = (await GetDataAsync(uri))[0]; + var data = await GetDataAsync(uri); + if (!data.Any()) + { + throw new InvalidTags(); + } + var parsingData = data[0]; + return new PostSearchResult( fileUrl: new($"{parsingData.FileUrl}"), From 6129e13dbb22451ed2fc1079a74f55c3d974d5a2 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 19:02:45 +0200 Subject: [PATCH 47/56] Fix crash for multiple image search --- BooruSharp/Booru/Impl/Rule34.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 072130c..91c6db7 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -28,7 +28,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) { // We don't have to handle what happen when there is no tag because it'll throw before - var url = new Uri($"{_imageUrl}&limit=1&id={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=0"); + var url = new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&json=0"); XmlDocument xml = await GetXmlAsync(url.AbsoluteUri); int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); From 6399db188f8137a099b71c2bb6e072dbca7ebbaf Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 19:04:43 +0200 Subject: [PATCH 48/56] Improve debug --- BooruSharp.UnitTests/Utils.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index bf66d49..39af1bc 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -237,10 +237,10 @@ public static async Task DoWebRequest(Func> method) public static async Task ValidatePostAsync(PostSearchResult res, string[] inputTags) { - if (res.FileUrl != null) Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri}"); - if (res.PreviewUrl != null) Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri}"); - if (res.PostUrl != null) Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri}"); - if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri}"); + if (res.FileUrl != null) Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri} for ID {res.ID}"); + if (res.PreviewUrl != null) Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri} for ID {res.ID}"); + if (res.PostUrl != null) Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri} for ID {res.ID}"); + if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri} for ID {res.ID}"); Assert.InRange(res.Rating, (Rating)(-1), Rating.Explicit); Assert.NotEmpty(res.Tags); Assert.NotEqual(0, res.ID); From a62116dc5233c5c28598c83d87944955e5436906 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 19:26:49 +0200 Subject: [PATCH 49/56] Fix more crashes --- BooruSharp.UnitTests/BooruTests.cs | 2 +- BooruSharp.UnitTests/Utils.cs | 3 ++- BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 29c3a14..6c948f9 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -97,7 +97,7 @@ public async Task GetImageByIdAsync(BooruTestData data) } else { - await Assert.ThrowsAsync(async () => { await booru.GetPostByIdAsync(data.ValidPostId); }); + await Assert.ThrowsAsync(async () => { await booru.GetPostByIdAsync(data.ValidPostId); }); } } } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 39af1bc..370390f 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -239,7 +239,8 @@ public static async Task ValidatePostAsync(PostSearchResult res, string[] inputT { if (res.FileUrl != null) Assert.True(await ValidateUrlAsync(res.FileUrl.AbsoluteUri), $"Invalid URL {res.FileUrl.AbsoluteUri} for ID {res.ID}"); if (res.PreviewUrl != null) Assert.True(await ValidateUrlAsync(res.PreviewUrl.AbsoluteUri), $"Invalid URL {res.PreviewUrl.AbsoluteUri} for ID {res.ID}"); - if (res.PostUrl != null) Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri} for ID {res.ID}"); + if (res.PostUrl != null && !res.PostUrl.AbsoluteUri.StartsWith("https://danbooru.donmai.us")) /* Danbooru always returns 403 here */ + Assert.True(await ValidateUrlAsync(res.PostUrl.AbsoluteUri), $"Invalid URL {res.PostUrl.AbsoluteUri} for ID {res.ID}"); if (res.SampleUri != null) Assert.True(await ValidateUrlAsync(res.SampleUri.AbsoluteUri), $"Invalid URL {res.SampleUri.AbsoluteUri} for ID {res.ID}"); Assert.InRange(res.Rating, (Rating)(-1), Rating.Explicit); Assert.NotEmpty(res.Tags); diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index f3ddd64..34aaf6a 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -43,7 +43,7 @@ protected override Task CreateRandomPostUriAsync(string[] tags) protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{_imageUrl}/{id}.json")); + return Task.FromResult(new Uri($"{APIBaseUrl}/{id}.json")); } /// diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index c11453e..ff56d22 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -34,7 +34,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{_imageUrl}&limit=1&id={id}")); + return Task.FromResult(new Uri($"{_imageUrl}&limit=1&id={id}&json=1")); } protected override async Task CreateRandomPostUriAsync(string[] tags) From cbd502764081f1e688bd5b7b614084c338463cb5 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Wed, 12 Apr 2023 19:39:04 +0200 Subject: [PATCH 50/56] Fix crashes --- BooruSharp/Booru/Impl/Twibooru.cs | 4 ++-- BooruSharp/Booru/Template/BooruOnRails.cs | 7 ++++--- BooruSharp/Booru/Template/Philomena.cs | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/BooruSharp/Booru/Impl/Twibooru.cs b/BooruSharp/Booru/Impl/Twibooru.cs index 2e5592d..aec4c4a 100644 --- a/BooruSharp/Booru/Impl/Twibooru.cs +++ b/BooruSharp/Booru/Impl/Twibooru.cs @@ -28,11 +28,11 @@ public Twibooru() : base("twibooru.org") private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); - if (!posts.Posts.Any()) + if (posts.Posts != null && !posts.Posts.Any()) { throw new InvalidTags(); } - var parsingData = posts.Posts[0]; + var parsingData = posts.Posts == null ? posts.Post : posts.Posts[0]; Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index c8dc2fd..725246e 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -41,7 +41,7 @@ protected override Task CreateRandomPostUriAsync(string[] tags) protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{_imageUrl}/{id}")); + return Task.FromResult(new Uri($"{APIBaseUrl}api/v3/posts/{id}")); } /// @@ -66,11 +66,11 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); - if (!posts.Posts.Any()) + if (posts.Posts != null && !posts.Posts.Any()) { throw new InvalidTags(); } - var parsingData = posts.Posts[0]; + var parsingData = posts.Posts == null ? posts.Post : posts.Posts[0]; Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; @@ -101,6 +101,7 @@ private protected override async Task GetPostSearchResultAsync public class PostContainer { + public SearchResult Post { init; get; } public SearchResult[] Posts { init; get; } } diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 9203e6a..8c3210d 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -33,17 +33,17 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{_imageUrl}/{id}")); + return Task.FromResult(new Uri($"{APIBaseUrl}api/v1/posts/{id}")); } private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); - if (!posts.Images.Any()) + if (posts.Images != null && !posts.Images.Any()) { throw new InvalidTags(); } - var parsingData = posts.Images[0]; + var parsingData = posts.Images == null ? posts.Image : posts.Images[0]; Rating rating; if (parsingData.Tags.Contains("explicit")) rating = Rating.Explicit; @@ -74,6 +74,7 @@ private protected override async Task GetPostSearchResultAsync public class PostContainer { + public SearchResult Image { init; get; } public SearchResult[] Images { init; get; } } From c94a69eae7297bb75472e8cc75ed3fe9d06bc26d Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 13 Apr 2023 01:51:31 +0200 Subject: [PATCH 51/56] Fix crash --- BooruSharp/Booru/Template/Danbooru.cs | 27 +++++++++++++++++++++----- BooruSharp/Booru/Template/Philomena.cs | 2 +- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 34aaf6a..5631c73 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using System.Net.Http; +using System.Text.Json; using System.Threading.Tasks; namespace BooruSharp.Booru.Template @@ -43,7 +44,7 @@ protected override Task CreateRandomPostUriAsync(string[] tags) protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{APIBaseUrl}/{id}.json")); + return Task.FromResult(new Uri($"{APIBaseUrl}posts/{id}.json")); } /// @@ -57,12 +58,28 @@ protected override void PreRequest(HttpRequestMessage message) private protected override async Task GetPostSearchResultAsync(Uri uri) { - var posts = await GetDataAsync(uri); - if (!posts.Any()) + var json = await GetDataAsync(uri); + SearchResult parsingData; + + if (json.ValueKind == JsonValueKind.Array) + { + var posts = JsonSerializer.Deserialize(json.GetRawText(), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }); + if (!posts.Any()) + { + throw new InvalidTags(); + } + parsingData = posts[0]; + } + else { - throw new InvalidTags(); + parsingData = JsonSerializer.Deserialize(json.GetRawText(), new JsonSerializerOptions + { + PropertyNamingPolicy = new SnakeCaseNamingPolicy() + }); } - var parsingData = posts[0]; return new PostSearchResult( fileUrl: parsingData.FileUrl != null ? new Uri(parsingData.FileUrl) : null, diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 8c3210d..38c0a7f 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -33,7 +33,7 @@ protected override Uri CreateQueryString(string query, string squery = "index") protected override Task CreatePostByIdUriAsync(int id) { - return Task.FromResult(new Uri($"{APIBaseUrl}api/v1/posts/{id}")); + return Task.FromResult(new Uri($"{APIBaseUrl}api/v1/json/images/{id}")); } private protected override async Task GetPostSearchResultAsync(Uri uri) From 835e090f735d0de1be314e921fb87ba54e486340 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 13 Apr 2023 01:56:21 +0200 Subject: [PATCH 52/56] Fix crash --- BooruSharp.UnitTests/BooruTests.cs | 10 +++++++ BooruSharp/Search/Post/ABooru.cs | 44 ++++++------------------------ 2 files changed, 18 insertions(+), 36 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 6c948f9..23d23ce 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -72,6 +72,16 @@ await Assert.ThrowsAsync(async () => { }); } + [SkippableTheory] + [MemberData(nameof(BooruParams))] + public async Task GetImageWithInvalidPostID(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + await Assert.ThrowsAsync(async () => { + await Utils.DoWebRequest(async () => { return await booru.GetPostByIdAsync(int.MaxValue); }); + }); + } + [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetExplicitImageAsync(BooruTestData data) diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index d502890..910a791 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -57,7 +57,14 @@ public virtual async Task GetPostByIdAsync(int id) throw new FeatureUnavailable(); var url = await CreatePostByIdUriAsync(id); - return await GetPostSearchResultAsync(url); + try + { + return await GetPostSearchResultAsync(url); + } + catch (InvalidTags) + { + throw new InvalidPostId(); + } } /* private const int _limitedTagsSearchCount = 2; @@ -122,41 +129,6 @@ public virtual async Task GetPostCountAsync(params string[] tagsArg) } } - /// - /// Searches for multiple random posts. If array is - /// specified and isn't empty, random posts containing those tags will be returned. - /// - /// The number of posts to get. - /// The optional array of tags that must be contained in the posts. - /// The task object representing the asynchronous operation. - /// - /// - /// - public virtual async Task GetRandomPostsAsync(int limit, params string[] tagsArg) - { - if (!HasMultipleRandomAPI) - throw new Search.FeatureUnavailable(); - - string[] tags = tagsArg != null - ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() - : Array.Empty(); - - if (NoMoreThanTwoTags && tags.Length > _limitedTagsSearchCount) - throw new Search.TooManyTags(); - - string tagString = TagsToString(tags); - - if (_format == UrlFormat.IndexPhp) - return await GetSearchResultsFromUrlAsync(CreateUrl(_imageUrl, GetLimit(limit), tagString) + "+sort:random"); - if (_format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails) - return await GetSearchResultsFromUrlAsync(CreateUrl(_imageUrl, GetLimit(limit), tagString, "sf=random")); - else if (NoMoreThanTwoTags) - // +order:random count as a tag so we use random=true instead to save one - return await GetSearchResultsFromUrlAsync(CreateUrl(_imageUrl, GetLimit(limit), tagString, "random=true")); - else - return await GetSearchResultsFromUrlAsync(CreateUrl(_imageUrl, GetLimit(limit), tagString) + "+order:random"); - } - /// /// Gets the latest posts on the website. If array is /// specified and isn't empty, latest posts containing those tags will be returned. From 34f5c8fa654c28edcb410265f4e226113f7373ad Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Thu, 13 Apr 2023 02:02:18 +0200 Subject: [PATCH 53/56] Fix tests --- BooruSharp.UnitTests/BooruTests.cs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 23d23ce..7fb57cb 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -72,16 +72,6 @@ await Assert.ThrowsAsync(async () => { }); } - [SkippableTheory] - [MemberData(nameof(BooruParams))] - public async Task GetImageWithInvalidPostID(BooruTestData data) - { - var booru = await Utils.GetAsync(data.BooruType); - await Assert.ThrowsAsync(async () => { - await Utils.DoWebRequest(async () => { return await booru.GetPostByIdAsync(int.MaxValue); }); - }); - } - [SkippableTheory] [MemberData(nameof(BooruParams))] public async Task GetExplicitImageAsync(BooruTestData data) @@ -110,5 +100,22 @@ public async Task GetImageByIdAsync(BooruTestData data) await Assert.ThrowsAsync(async () => { await booru.GetPostByIdAsync(data.ValidPostId); }); } } + + [SkippableTheory] + [MemberData(nameof(BooruParams))] + public async Task GetImageWithInvalidPostID(BooruTestData data) + { + var booru = await Utils.GetAsync(data.BooruType); + if (booru.HasPostByIdAPI) + { + await Assert.ThrowsAsync(async () => { + await Utils.DoWebRequest(async () => { return await booru.GetPostByIdAsync(int.MaxValue); }); + }); + } + else + { + await Assert.ThrowsAsync(async () => { await booru.GetPostByIdAsync(int.MaxValue); }); + } + } } } From 4f13e5ffaa98f34896308c57bac1920cabd8cda3 Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Mon, 21 Aug 2023 22:31:09 +0200 Subject: [PATCH 54/56] Rename exception --- BooruSharp.UnitTests/BooruTests.cs | 4 +- BooruSharp.UnitTests/Utils.cs | 2 +- BooruSharp.sln | 10 +--- BooruSharp/Booru/ABooru.cs | 7 ++- BooruSharp/Booru/Impl/Rule34.cs | 4 +- BooruSharp/Booru/Impl/Twibooru.cs | 2 +- BooruSharp/Booru/Template/BooruOnRails.cs | 2 +- BooruSharp/Booru/Template/Danbooru.cs | 2 +- BooruSharp/Booru/Template/E621.cs | 2 +- BooruSharp/Booru/Template/Gelbooru.cs | 2 +- BooruSharp/Booru/Template/Gelbooru02.cs | 4 +- BooruSharp/Booru/Template/Moebooru.cs | 2 +- BooruSharp/Booru/Template/MyImouto.cs | 2 +- BooruSharp/Booru/Template/Philomena.cs | 2 +- BooruSharp/Booru/Template/Sankaku.cs | 2 +- BooruSharp/BooruSharp.csproj | 2 +- ...InvalidTags.cs => InvalidPostException.cs} | 20 +++---- BooruSharp/Search/InvalidPostId.cs | 52 ------------------- BooruSharp/Search/Post/ABooru.cs | 9 +--- 19 files changed, 36 insertions(+), 96 deletions(-) rename BooruSharp/Search/{InvalidTags.cs => InvalidPostException.cs} (78%) delete mode 100644 BooruSharp/Search/InvalidPostId.cs diff --git a/BooruSharp.UnitTests/BooruTests.cs b/BooruSharp.UnitTests/BooruTests.cs index 7fb57cb..fd58561 100644 --- a/BooruSharp.UnitTests/BooruTests.cs +++ b/BooruSharp.UnitTests/BooruTests.cs @@ -67,7 +67,7 @@ public async Task GetRandomImageWith3TagsAsync(BooruTestData data) public async Task GetInvalidImageAsync(BooruTestData data) { var booru = await Utils.GetAsync(data.BooruType); - await Assert.ThrowsAsync(async () => { + await Assert.ThrowsAsync(async () => { await Utils.DoWebRequest(async () => { return await booru.GetRandomPostAsync("azazazazaz"); }); }); } @@ -108,7 +108,7 @@ public async Task GetImageWithInvalidPostID(BooruTestData data) var booru = await Utils.GetAsync(data.BooruType); if (booru.HasPostByIdAPI) { - await Assert.ThrowsAsync(async () => { + await Assert.ThrowsAsync(async () => { await Utils.DoWebRequest(async () => { return await booru.GetPostByIdAsync(int.MaxValue); }); }); } diff --git a/BooruSharp.UnitTests/Utils.cs b/BooruSharp.UnitTests/Utils.cs index 370390f..fd0ea21 100644 --- a/BooruSharp.UnitTests/Utils.cs +++ b/BooruSharp.UnitTests/Utils.cs @@ -165,7 +165,7 @@ public static class Utils BooruType = typeof(Yandere), Tags = new[] { "kantai_collection", "sweater", "bra" }, ValidPostId = 1052829, - ExplicitTag = "pussy" + ExplicitTag = "cervix" } }, //new BooruTestData() { BooruType = typeof(Pixiv) } diff --git a/BooruSharp.sln b/BooruSharp.sln index 94b3858..fc668bc 100644 --- a/BooruSharp.sln +++ b/BooruSharp.sln @@ -1,14 +1,12 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29806.167 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33723.286 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BooruSharp", "BooruSharp\BooruSharp.csproj", "{76215AC0-5BEB-4B79-AE41-0027400900FB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BooruSharp.UnitTests", "BooruSharp.UnitTests\BooruSharp.UnitTests.csproj", "{2E4A5791-6CF6-4514-AED0-EBA120B6DE83}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BooruSharp.Others", "BooruSharp.Others\BooruSharp.Others.csproj", "{A151CEAA-A3F9-4948-8B2D-321C36E9BB7E}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,10 +21,6 @@ Global {2E4A5791-6CF6-4514-AED0-EBA120B6DE83}.Debug|Any CPU.Build.0 = Debug|Any CPU {2E4A5791-6CF6-4514-AED0-EBA120B6DE83}.Release|Any CPU.ActiveCfg = Release|Any CPU {2E4A5791-6CF6-4514-AED0-EBA120B6DE83}.Release|Any CPU.Build.0 = Release|Any CPU - {A151CEAA-A3F9-4948-8B2D-321C36E9BB7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A151CEAA-A3F9-4948-8B2D-321C36E9BB7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A151CEAA-A3F9-4948-8B2D-321C36E9BB7E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A151CEAA-A3F9-4948-8B2D-321C36E9BB7E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 9d5c528..a3ef46c 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -77,7 +77,12 @@ protected virtual async Task GetDataArray(Uri url) protected virtual async Task GetDataAsync(Uri url) { - return JsonSerializer.Deserialize(await GetJsonAsync(url), new JsonSerializerOptions + var res = await GetJsonAsync(url); + if (string.IsNullOrEmpty(res)) + { + throw new InvalidPostException(); + } + return JsonSerializer.Deserialize(res, new JsonSerializerOptions { PropertyNamingPolicy = new SnakeCaseNamingPolicy() }); diff --git a/BooruSharp/Booru/Impl/Rule34.cs b/BooruSharp/Booru/Impl/Rule34.cs index 91c6db7..82d5e10 100644 --- a/BooruSharp/Booru/Impl/Rule34.cs +++ b/BooruSharp/Booru/Impl/Rule34.cs @@ -33,7 +33,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) - throw new InvalidTags(); + throw new InvalidPostException(); // The limit is in fact 200000 but search with tags make it incredibly hard to know what is really your pid if (max > 20001) @@ -50,7 +50,7 @@ private protected override async Task GetPostSearchResultAsync var data = await GetDataAsync(uri); if (!data.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = data[0]; diff --git a/BooruSharp/Booru/Impl/Twibooru.cs b/BooruSharp/Booru/Impl/Twibooru.cs index aec4c4a..ae41ad2 100644 --- a/BooruSharp/Booru/Impl/Twibooru.cs +++ b/BooruSharp/Booru/Impl/Twibooru.cs @@ -30,7 +30,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (posts.Posts != null && !posts.Posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts.Posts == null ? posts.Post : posts.Posts[0]; diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 725246e..0fd9b27 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -68,7 +68,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (posts.Posts != null && !posts.Posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts.Posts == null ? posts.Post : posts.Posts[0]; diff --git a/BooruSharp/Booru/Template/Danbooru.cs b/BooruSharp/Booru/Template/Danbooru.cs index 5631c73..6cb796e 100644 --- a/BooruSharp/Booru/Template/Danbooru.cs +++ b/BooruSharp/Booru/Template/Danbooru.cs @@ -69,7 +69,7 @@ private protected override async Task GetPostSearchResultAsync }); if (!posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } parsingData = posts[0]; } diff --git a/BooruSharp/Booru/Template/E621.cs b/BooruSharp/Booru/Template/E621.cs index 60fc84a..fcee489 100644 --- a/BooruSharp/Booru/Template/E621.cs +++ b/BooruSharp/Booru/Template/E621.cs @@ -63,7 +63,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (!posts.Posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts.Posts[0]; diff --git a/BooruSharp/Booru/Template/Gelbooru.cs b/BooruSharp/Booru/Template/Gelbooru.cs index ec9798a..6d1088f 100644 --- a/BooruSharp/Booru/Template/Gelbooru.cs +++ b/BooruSharp/Booru/Template/Gelbooru.cs @@ -54,7 +54,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (posts.Post == null) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts.Post[0]; diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index ff56d22..b7b2ca2 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -51,7 +51,7 @@ protected override async Task CreateRandomPostUriAsync(string[] tags) int max = int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); if (max == 0) - throw new InvalidTags(); + throw new InvalidPostException(); return new Uri($"{_imageUrl}&limit=1&tags={string.Join("+", tags.Select(Uri.EscapeDataString)).ToLowerInvariant()}&pid={Random.Next(0, max)}&json=1"); } @@ -70,7 +70,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (!posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts[0]; diff --git a/BooruSharp/Booru/Template/Moebooru.cs b/BooruSharp/Booru/Template/Moebooru.cs index 7bbe13f..4c87a07 100644 --- a/BooruSharp/Booru/Template/Moebooru.cs +++ b/BooruSharp/Booru/Template/Moebooru.cs @@ -53,7 +53,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (!posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts[0]; diff --git a/BooruSharp/Booru/Template/MyImouto.cs b/BooruSharp/Booru/Template/MyImouto.cs index 8c6b74c..cb630ab 100644 --- a/BooruSharp/Booru/Template/MyImouto.cs +++ b/BooruSharp/Booru/Template/MyImouto.cs @@ -28,7 +28,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (!posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts[0]; diff --git a/BooruSharp/Booru/Template/Philomena.cs b/BooruSharp/Booru/Template/Philomena.cs index 38c0a7f..0497426 100644 --- a/BooruSharp/Booru/Template/Philomena.cs +++ b/BooruSharp/Booru/Template/Philomena.cs @@ -41,7 +41,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (posts.Images != null && !posts.Images.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts.Images == null ? posts.Image : posts.Images[0]; diff --git a/BooruSharp/Booru/Template/Sankaku.cs b/BooruSharp/Booru/Template/Sankaku.cs index 475a2b9..fd148bf 100644 --- a/BooruSharp/Booru/Template/Sankaku.cs +++ b/BooruSharp/Booru/Template/Sankaku.cs @@ -57,7 +57,7 @@ private protected override async Task GetPostSearchResultAsync var posts = await GetDataAsync(uri); if (!posts.Any()) { - throw new InvalidTags(); + throw new InvalidPostException(); } var parsingData = posts[0]; diff --git a/BooruSharp/BooruSharp.csproj b/BooruSharp/BooruSharp.csproj index b8d09df..cd9af0a 100644 --- a/BooruSharp/BooruSharp.csproj +++ b/BooruSharp/BooruSharp.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 true 4.0.0-beta Xwilarg diff --git a/BooruSharp/Search/InvalidTags.cs b/BooruSharp/Search/InvalidPostException.cs similarity index 78% rename from BooruSharp/Search/InvalidTags.cs rename to BooruSharp/Search/InvalidPostException.cs index fb972ae..c61d0d7 100644 --- a/BooruSharp/Search/InvalidTags.cs +++ b/BooruSharp/Search/InvalidPostException.cs @@ -8,44 +8,44 @@ namespace BooruSharp.Search /// tag query to perform an API request is called with the invalid query. /// [Serializable] - public class InvalidTags : ArgumentException + public class InvalidPostException : ArgumentException { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - public InvalidTags() - : base("There is nothing available with these tags.") + public InvalidPostException() + : base("There is nothing available with these tags or id.") { } /// - /// Initializes a new instance of the + /// Initializes a new instance of the /// class with a specified error message. /// /// The error message that explains the reason for the exception. - public InvalidTags(string message) + public InvalidPostException(string message) : base(message) { } /// - /// Initializes a new instance of the class with a specified + /// Initializes a new instance of the class with a specified /// error message and a reference to the inner exception that is the cause of this exception. /// /// The error message that explains the reason for the exception. /// The exception that is the cause of the current exception, /// or a reference if no inner exception is specified. - public InvalidTags(string message, Exception innerException) + public InvalidPostException(string message, Exception innerException) : base(message, innerException) { } /// - /// Initializes a new instance of the + /// Initializes a new instance of the /// class with serialized data. /// /// The that holds the /// serialized object data about the exception being thrown. /// The that contains /// contextual information about the source or destination. - protected InvalidTags(SerializationInfo info, StreamingContext context) + protected InvalidPostException(SerializationInfo info, StreamingContext context) : base(info, context) { } } diff --git a/BooruSharp/Search/InvalidPostId.cs b/BooruSharp/Search/InvalidPostId.cs deleted file mode 100644 index 9a1cb40..0000000 --- a/BooruSharp/Search/InvalidPostId.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace BooruSharp.Search -{ - /// - /// Represents errors that occur whenever a method that requires a - /// post ID to perform an API request is called with the invalid ID. - /// - [Serializable] - public class InvalidPostId : ArgumentException - { - /// - /// Initializes a new instance of the class. - /// - public InvalidPostId() - : base("There is no post with this ID.") - { } - - /// - /// Initializes a new instance of the - /// class with a specified error message. - /// - /// The error message that explains the reason for the exception. - public InvalidPostId(string message) - : base(message) - { } - - /// - /// Initializes a new instance of the class with a specified - /// error message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, - /// or a reference if no inner exception is specified. - public InvalidPostId(string message, Exception innerException) - : base(message, innerException) - { } - - /// - /// Initializes a new instance of the - /// class with serialized data. - /// - /// The that holds the - /// serialized object data about the exception being thrown. - /// The that contains - /// contextual information about the source or destination. - protected InvalidPostId(SerializationInfo info, StreamingContext context) - : base(info, context) - { } - } -} diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 910a791..0f8d1e9 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -57,14 +57,7 @@ public virtual async Task GetPostByIdAsync(int id) throw new FeatureUnavailable(); var url = await CreatePostByIdUriAsync(id); - try - { - return await GetPostSearchResultAsync(url); - } - catch (InvalidTags) - { - throw new InvalidPostId(); - } + return await GetPostSearchResultAsync(url); } /* private const int _limitedTagsSearchCount = 2; From 5f00912f83abd66c59ee47c7452a501a3c5f45eb Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Mon, 21 Aug 2023 22:38:34 +0200 Subject: [PATCH 55/56] Fix posts not being found --- BooruSharp/Booru/ABooru.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index a3ef46c..8ae03e0 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -131,6 +131,11 @@ private protected async Task GetJsonAsync(string url) PreRequest(message); var msg = await HttpClient.SendAsync(message); + if (msg.StatusCode == HttpStatusCode.NotFound) + { + throw new InvalidPostException(); + } + msg.EnsureSuccessStatusCode(); return await msg.Content.ReadAsStringAsync(); From 9e78401cbd9d271afc082c0e811f0a9f2c10b10d Mon Sep 17 00:00:00 2001 From: Xwilarg Date: Mon, 21 Aug 2023 23:15:25 +0200 Subject: [PATCH 56/56] Begin to convert tag count endpoint --- BooruSharp/Booru/ABooru.cs | 9 ++- BooruSharp/Booru/Template/BooruOnRails.cs | 16 ++++++ BooruSharp/Booru/Template/Gelbooru02.cs | 1 - BooruSharp/Search/Post/ABooru.cs | 70 +++++++++++------------ README.md | 4 -- 5 files changed, 57 insertions(+), 43 deletions(-) diff --git a/BooruSharp/Booru/ABooru.cs b/BooruSharp/Booru/ABooru.cs index 8ae03e0..f5d7542 100644 --- a/BooruSharp/Booru/ABooru.cs +++ b/BooruSharp/Booru/ABooru.cs @@ -15,6 +15,7 @@ public abstract partial class ABooru { public virtual bool CanSearchWithNoTag => true; public virtual bool HasPostByIdAPI => true; + public virtual bool HasPostCountAPI => true; public virtual int MaxNumberOfTags => -1; public virtual Uri FileBaseUrl => APIBaseUrl; @@ -25,10 +26,11 @@ public abstract partial class ABooru /// public abstract bool IsSafe { get; } - private protected virtual Search.Comment.SearchResult GetCommentSearchResultAsync(Uri uri) - => throw new FeatureUnavailable(); + private protected abstract Task GetPostSearchResultAsync(Uri uri); - private protected virtual Task GetPostSearchResultAsync(Uri uri) + private protected abstract Task GetPostCountSearchResultAsync(Uri uri); + + private protected virtual Search.Comment.SearchResult GetCommentSearchResultAsync(Uri uri) => throw new FeatureUnavailable(); private protected virtual Search.Related.SearchResult GetRelatedSearchResultAsync(Uri uri) @@ -66,6 +68,7 @@ protected virtual void PreRequest(HttpRequestMessage message) /// List of tags sent by the user protected abstract Task CreateRandomPostUriAsync(string[] tags); protected abstract Task CreatePostByIdUriAsync(int id); + protected abstract Task CreatePostCountUrlAsync(string[] tags); protected virtual async Task GetDataArray(Uri url) { diff --git a/BooruSharp/Booru/Template/BooruOnRails.cs b/BooruSharp/Booru/Template/BooruOnRails.cs index 0fd9b27..ddc25a6 100644 --- a/BooruSharp/Booru/Template/BooruOnRails.cs +++ b/BooruSharp/Booru/Template/BooruOnRails.cs @@ -44,6 +44,11 @@ protected override Task CreatePostByIdUriAsync(int id) return Task.FromResult(new Uri($"{APIBaseUrl}api/v3/posts/{id}")); } + protected override Task CreatePostCountUrlAsync(string[] tags) + { + return Task.FromResult(new Uri($"{_imageUrl}?limit=1&q={string.Join(",", tags.Select(Uri.EscapeDataString).Select(x => x.Replace('_', '+'))).ToLowerInvariant()}")); + } + /// /// ID used to set filter and have access to as many posts as possible /// @@ -63,6 +68,12 @@ protected override void PreRequest(HttpRequestMessage message) message.RequestUri = new Uri(uriBuilder.ToString()); } + private protected override async Task GetPostCountSearchResultAsync(Uri uri) + { + var data = await GetDataAsync(uri); + return data.Total; + } + private protected override async Task GetPostSearchResultAsync(Uri uri) { var posts = await GetDataAsync(uri); @@ -99,6 +110,11 @@ private protected override async Task GetPostSearchResultAsync ); } + public class PostsCount + { + public int Total { init; get; } + } + public class PostContainer { public SearchResult Post { init; get; } diff --git a/BooruSharp/Booru/Template/Gelbooru02.cs b/BooruSharp/Booru/Template/Gelbooru02.cs index b7b2ca2..9d48e72 100644 --- a/BooruSharp/Booru/Template/Gelbooru02.cs +++ b/BooruSharp/Booru/Template/Gelbooru02.cs @@ -3,7 +3,6 @@ using System; using System.Linq; using System.Net.Http; -using System.Security.Cryptography.X509Certificates; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; diff --git a/BooruSharp/Search/Post/ABooru.cs b/BooruSharp/Search/Post/ABooru.cs index 0f8d1e9..26cbf35 100644 --- a/BooruSharp/Search/Post/ABooru.cs +++ b/BooruSharp/Search/Post/ABooru.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading.Tasks; +using System.Xml; using static BooruSharp.Booru.Template.E621; namespace BooruSharp.Booru @@ -59,6 +60,40 @@ public virtual async Task GetPostByIdAsync(int id) var url = await CreatePostByIdUriAsync(id); return await GetPostSearchResultAsync(url); } + + /// + /// Gets the total number of available posts. If array is specified + /// and isn't empty, the total number of posts containing these tags will be returned. + /// + /// The optional array of tags. + /// The task object representing the asynchronous operation. + /// + /// + /// + public virtual async Task GetPostCountAsync(params string[] tagsArg) + { + if (!HasPostCountAPI) + throw new FeatureUnavailable(); + + string[] tags = tagsArg != null + ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() + : Array.Empty(); + + if (MaxNumberOfTags != -1 && tags.Length > MaxNumberOfTags) + { + throw new TooManyTags(MaxNumberOfTags); + } + + var url = await CreatePostCountUrlAsync(tags); + return await GetPostCountSearchResultAsync(url); + /* + else + { + var url = CreateUrl(_imageUrlXml, GetLimit(1), TagsToString(tags)); + XmlDocument xml = await GetXmlAsync(url); + return int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); + }*/ + } /* private const int _limitedTagsSearchCount = 2; private const int _increasedPostLimitCount = 20001; @@ -86,41 +121,6 @@ private string GetLimit(int quantity) } - /// - /// Gets the total number of available posts. If array is specified - /// and isn't empty, the total number of posts containing these tags will be returned. - /// - /// The optional array of tags. - /// The task object representing the asynchronous operation. - /// - /// - /// - public virtual async Task GetPostCountAsync(params string[] tagsArg) - { - if (!HasPostCountAPI) - throw new Search.FeatureUnavailable(); - - string[] tags = tagsArg != null - ? tagsArg.Where(tag => !string.IsNullOrWhiteSpace(tag)).ToArray() - : Array.Empty(); - - if (NoMoreThanTwoTags && tags.Length > _limitedTagsSearchCount) - throw new Search.TooManyTags(); - - if (_format == UrlFormat.Philomena || _format == UrlFormat.BooruOnRails) - { - var url = CreateUrl(_imageUrl, GetLimit(1), TagsToString(tags)); - var json = await GetJsonAsync(url); - var token = (JToken)JsonConvert.DeserializeObject(json); - return token["total"].Value(); - } - else - { - var url = CreateUrl(_imageUrlXml, GetLimit(1), TagsToString(tags)); - XmlDocument xml = await GetXmlAsync(url); - return int.Parse(xml.ChildNodes.Item(1).Attributes[0].InnerXml); - } - } /// /// Gets the latest posts on the website. If array is diff --git a/README.md b/README.md index e5e5b3e..998e90d 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,6 @@ | [![NuGet](https://img.shields.io/nuget/v/BooruSharp.svg)](https://www.nuget.org/packages/BooruSharp/) | [![NuGet](https://img.shields.io/nuget/v/BooruSharp.Others.svg)](https://www.nuget.org/packages/BooruSharp.Others/) | | ![Nuget](https://img.shields.io/nuget/dt/BooruSharp) | ![Nuget](https://img.shields.io/nuget/dt/BooruSharp.Others) | -| CI | Code Quality | Coverage | -| -- | ------------ | -------- | -| [![Build (GitHub CI)](https://github.com/Xwilarg/BooruSharp/workflows/.NET%20Core/badge.svg)](https://github.com/Xwilarg/BooruSharp/actions) | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/07be9e8c69cd4d87b20987b9fcec7a0e)](https://www.codacy.com/manual/Xwilarg/BooruSharp?utm_source=github.com&utm_medium=referral&utm_content=Xwilarg/BooruSharp&utm_campaign=Badge_Grade) | [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/07be9e8c69cd4d87b20987b9fcec7a0e)](https://www.codacy.com/manual/Xwilarg/BooruSharp?utm_source=github.com&utm_medium=referral&utm_content=Xwilarg/BooruSharp&utm_campaign=Badge_Coverage) | - # BooruSharp BooruSharp is a C# library to browse Booru websites easily
You can download it from [NuGet](https://www.nuget.org/packages/BooruSharp):