diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 0000000..95fcb99 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,187 @@ +root = true + +[*.cs] + +indent_size = 4 +indent_style = space +tab_width = 4 + +end_of_line = crlf +insert_final_newline = false + +dotnet_separate_import_directive_groups = true +dotnet_sort_system_directives_first = false +file_header_template = unset + +dotnet_style_qualification_for_event = true +dotnet_style_qualification_for_field = true +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = true + +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = always_for_clarity +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = false +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +dotnet_style_readonly_field = true + +dotnet_code_quality_unused_parameters = all + +dotnet_remove_unnecessary_suppression_exclusions = none + +dotnet_style_allow_multiple_blank_lines_experimental = false +dotnet_style_allow_statement_immediately_after_block_experimental = false + +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = false +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = false +csharp_style_prefer_switch_expression = true + +csharp_style_conditional_delegate_call = true + +csharp_prefer_static_local_function = false +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = false + +csharp_prefer_simple_default_expression = false +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +csharp_using_directive_placement = outside_namespace + +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = false +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/src/R34Sharp.Experimental/Program.cs b/src/R34Sharp.Experimental/Program.cs index 54d42a5..9ed7d64 100644 --- a/src/R34Sharp.Experimental/Program.cs +++ b/src/R34Sharp.Experimental/Program.cs @@ -1,4 +1,7 @@ -using R34Sharp; +using R34Sharp.Entities.Posts; +using R34Sharp.Enums; +using R34Sharp.Models; +using R34Sharp.Search; namespace R34Sharp.Experimental { @@ -6,19 +9,21 @@ internal static class Program { private static string AssetsDirectory => Path.Combine(Directory.GetCurrentDirectory(), "R34Assets"); - private static R34ApiClient _client = new(); + private static readonly R34ApiClient _client = new(); [MTAThread] - private static async Task Main() { + private static async Task Main() + { if (!Directory.Exists(AssetsDirectory)) { - Directory.CreateDirectory(AssetsDirectory); + _ = Directory.CreateDirectory(AssetsDirectory); } - R34PostsSearchBuilder searchBuilder = new() { + R34PostsSearchBuilder searchBuilder = new() + { Limit = 100, - Tags = new R34TagModel[] + Tags = new R34FormattedTag[] { new("Bara"), }, @@ -28,15 +33,18 @@ private static async Task Main() { Console.WriteLine(" [ STARTING ] "); Console.WriteLine($"Path: {AssetsDirectory}"); + R34Posts posts = await _client.Posts.GetPostsByFilterAsync(searchBuilder, x => x.FileType == R34FileType.Image); + Console.WriteLine($"Request Completed!"); + int count = 0; - foreach (R34Post post in (await _client.Posts.GetPostsByFilterAsync(searchBuilder, x => x.Rating == R34Rating.Questionable)).Data) + foreach (R34Post post in posts.Data) { using MemoryStream ms = await post.DownloadFileAsync(); byte[] r34FileByteArray = ms.ToArray(); await File.WriteAllBytesAsync(Path.Combine(AssetsDirectory, $"{post.FileName}{post.FileExtension}"), r34FileByteArray); - Console.WriteLine($"File #{count} Donwloaded! ({(r34FileByteArray.Length / (Math.Pow(1024, 2))).ToString("0.###")}mb)"); + Console.WriteLine($"File #{count} Donwloaded! ({r34FileByteArray.Length / Math.Pow(1024, 2):0.###}mb)"); count++; } } diff --git a/src/R34Sharp.Tests/R34PostsTest.cs b/src/R34Sharp.Tests/Posts/R34PostsTests.cs similarity index 66% rename from src/R34Sharp.Tests/R34PostsTest.cs rename to src/R34Sharp.Tests/Posts/R34PostsTests.cs index 23bdec2..8bc0c6c 100644 --- a/src/R34Sharp.Tests/R34PostsTest.cs +++ b/src/R34Sharp.Tests/Posts/R34PostsTests.cs @@ -1,13 +1,18 @@ -namespace R34Sharp.Tests +using R34Sharp.Entities.Posts; +using R34Sharp.Enums; +using R34Sharp.Models; +using R34Sharp.Search; + +namespace R34Sharp.Tests.Posts { - public class R34PostsTest + public class R34PostsTests { private static readonly R34ApiClient _client = new(); - private static readonly R34TagModel[] tagsPrefab = new R34TagModel[] + private static readonly R34FormattedTag[] tagsPrefab = new R34FormattedTag[] { new("Little Mac"), }; - private static readonly R34TagModel[] blockedTagsPrefab = new R34TagModel[] + private static readonly R34FormattedTag[] blockedTagsPrefab = new R34FormattedTag[] { new("Looking At Viewer"), }; @@ -32,9 +37,9 @@ public async Task Filter_All_Posts_To_Get_Videos_Async() Limit = 1000, Tags = tagsPrefab - }, x => x.FileType == FileType.Video); + }, x => x.FileType == R34FileType.Video); - Assert.All(posts.Data, x => Assert.True(x.FileType == FileType.Video)); + Assert.All(posts.Data, x => Assert.True(x.FileType == R34FileType.Video)); } [Fact] @@ -44,7 +49,7 @@ public async Task Blocking_Posts_That_Contain_Certain_Tags_Async() { Limit = 1000, Tags = tagsPrefab, - BlockedTags = new(blockedTagsPrefab) + BlockedTags = blockedTagsPrefab, }); Assert.All(posts.Data, x => Assert.False(x.HasTag(new("Looking At Viewer")))); @@ -53,18 +58,18 @@ public async Task Blocking_Posts_That_Contain_Certain_Tags_Async() [Fact] public async Task Block_Requests_Outside_The_Limit_Range_Async() { - await Assert.ThrowsAnyAsync(async () => + _ = await Assert.ThrowsAnyAsync(async () => { - await _client.Posts.GetPostsAsync(new() + _ = await _client.Posts.GetPostsAsync(new() { Limit = 0, Tags = tagsPrefab }); }); - await Assert.ThrowsAnyAsync(async () => + _ = await Assert.ThrowsAnyAsync(async () => { - await _client.Posts.GetPostsAsync(new() + _ = await _client.Posts.GetPostsAsync(new() { Limit = 1001, Tags = tagsPrefab @@ -75,12 +80,12 @@ await _client.Posts.GetPostsAsync(new() [Fact] public async Task Block_Requests_Without_Tags_Async() { - await Assert.ThrowsAnyAsync(async () => + _ = await Assert.ThrowsAnyAsync(async () => { - await _client.Posts.GetPostsAsync(new() + _ = await _client.Posts.GetPostsAsync(new() { Limit = 0, - Tags = Array.Empty() + Tags = Array.Empty() }); }); } diff --git a/src/R34Sharp.Tests/Search/R34CommentsSearchBuilderTests.cs b/src/R34Sharp.Tests/Search/R34CommentsSearchBuilderTests.cs new file mode 100644 index 0000000..3f331c2 --- /dev/null +++ b/src/R34Sharp.Tests/Search/R34CommentsSearchBuilderTests.cs @@ -0,0 +1,48 @@ +using R34Sharp.Search; + +namespace R34Sharp.Tests.Search +{ + public sealed class R34CommentsSearchBuilderTests + { + [Fact] + public void Constructor_DefaultValues_Success() + { + // Arrange + R34CommentsSearchBuilder searchBuilder = new(); + + // Act & Assert + Assert.Equal(0ul, searchBuilder.PostId); + } + + [Fact] + public void WithId_ValidValue_ReturnsBuilderWithId() + { + // Arrange + R34CommentsSearchBuilder searchBuilder = new(); + + // Act + R34CommentsSearchBuilder result = searchBuilder.WithId(42); + + // Assert + Assert.Equal(42ul, searchBuilder.PostId); + Assert.Same(searchBuilder, result); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(999)] + public void WithId_SetValidValues_Success(ulong id) + { + // Arrange + R34CommentsSearchBuilder searchBuilder = new(); + + // Act + R34CommentsSearchBuilder result = searchBuilder.WithId(id); + + // Assert + Assert.Equal(id, searchBuilder.PostId); + Assert.Same(searchBuilder, result); + } + } +} diff --git a/src/R34Sharp.Tests/Search/R34PostsSearchBuilderTests.cs b/src/R34Sharp.Tests/Search/R34PostsSearchBuilderTests.cs new file mode 100644 index 0000000..51da3f6 --- /dev/null +++ b/src/R34Sharp.Tests/Search/R34PostsSearchBuilderTests.cs @@ -0,0 +1,110 @@ +using R34Sharp.Models; +using R34Sharp.Search; + +namespace R34Sharp.Tests.Search +{ + public class R34PostsSearchBuilderTests + { + [Fact] + public void Constructor_DefaultValues_Success() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + + // Act & Assert + Assert.Equal(100, searchBuilder.Limit); + Assert.Equal(0ul, searchBuilder.Id.Value); + Assert.Equal(0, searchBuilder.Offset.Value); + Assert.Empty(searchBuilder.Tags); + Assert.Empty(searchBuilder.BlockedTags); + } + + [Fact] + public void WithLimit_ValidValue_ReturnsBuilderWithLimit() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + + // Act + R34PostsSearchBuilder result = searchBuilder.WithLimit(200); + + // Assert + Assert.Equal(200, searchBuilder.Limit); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithId_ValidValue_ReturnsBuilderWithId() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + + // Act + R34PostsSearchBuilder result = searchBuilder.WithId(42); + + // Assert + Assert.Equal(42ul, searchBuilder.Id.Value); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithOffset_ValidValue_ReturnsBuilderWithOffset() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + + // Act + R34PostsSearchBuilder result = searchBuilder.WithOffset(5); + + // Assert + Assert.Equal(5, searchBuilder.Offset.Value); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithTags_ValidValue_ReturnsBuilderWithTags() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + R34FormattedTag[] tags = new R34FormattedTag[] { new("tag1"), new("tag2") }; + + // Act + R34PostsSearchBuilder result = searchBuilder.WithTags(tags); + + // Assert + Assert.Equal(tags, searchBuilder.Tags); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithBlockedTags_ValidValue_ReturnsBuilderWithBlockedTags() + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + R34FormattedTag[] blockedTags = new R34FormattedTag[] { new("blocked1"), new("blocked2") }; + + // Act + R34PostsSearchBuilder result = searchBuilder.WithBlockedTags(blockedTags); + + // Assert + Assert.Equal(blockedTags, searchBuilder.BlockedTags); + Assert.Same(searchBuilder, result); + } + + [Theory] + [InlineData(1)] + [InlineData(1000)] + public void WithLimit_SetValidValues_Success(int limit) + { + // Arrange + R34PostsSearchBuilder searchBuilder = new(); + + // Act + R34PostsSearchBuilder result = searchBuilder.WithLimit(limit); + + // Assert + Assert.Equal(limit, searchBuilder.Limit); + Assert.Same(searchBuilder, result); + } + } +} \ No newline at end of file diff --git a/src/R34Sharp.Tests/Search/R34TagsSearchBuilderTests.cs b/src/R34Sharp.Tests/Search/R34TagsSearchBuilderTests.cs new file mode 100644 index 0000000..7da74fd --- /dev/null +++ b/src/R34Sharp.Tests/Search/R34TagsSearchBuilderTests.cs @@ -0,0 +1,79 @@ +using R34Sharp.Enums; +using R34Sharp.Search; + +namespace R34Sharp.Tests.Search +{ + public sealed class R34TagsSearchBuilderTests + { + [Fact] + public void Constructor_DefaultValues_Success() + { + // Arrange + R34TagsSearchBuilder searchBuilder = new(); + + // Act & Assert + Assert.Equal(R34TagSearchType.Name, searchBuilder.SearchType); + Assert.Empty(searchBuilder.Search); + Assert.Equal(100, searchBuilder.Limit); + } + + [Fact] + public void WithSearchType_ValidValue_ReturnsBuilderWithSearchType() + { + // Arrange + R34TagsSearchBuilder searchBuilder = new(); + + // Act + R34TagsSearchBuilder result = searchBuilder.WithSearchType(R34TagSearchType.Pattern); + + // Assert + Assert.Equal(R34TagSearchType.Pattern, searchBuilder.SearchType); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithSearch_ValidValue_ReturnsBuilderWithSearch() + { + // Arrange + R34TagsSearchBuilder searchBuilder = new(); + + // Act + R34TagsSearchBuilder result = searchBuilder.WithSearch("tag123"); + + // Assert + Assert.Equal("tag123", searchBuilder.Search); + Assert.Same(searchBuilder, result); + } + + [Fact] + public void WithLimit_ValidValue_ReturnsBuilderWithLimit() + { + // Arrange + R34TagsSearchBuilder searchBuilder = new(); + + // Act + R34TagsSearchBuilder result = searchBuilder.WithLimit(50); + + // Assert + Assert.Equal(50, searchBuilder.Limit); + Assert.Same(searchBuilder, result); + } + + [Theory] + [InlineData(R34TagSearchType.Name)] + [InlineData(R34TagSearchType.Pattern)] + [InlineData(R34TagSearchType.Id)] + public void WithSearchType_SetValidValues_Success(R34TagSearchType searchType) + { + // Arrange + R34TagsSearchBuilder searchBuilder = new(); + + // Act + R34TagsSearchBuilder result = searchBuilder.WithSearchType(searchType); + + // Assert + Assert.Equal(searchType, searchBuilder.SearchType); + Assert.Same(searchBuilder, result); + } + } +} diff --git a/src/R34Sharp/Components/R34BaseApiComponent.cs b/src/R34Sharp/Components/R34BaseApiComponent.cs index 14dadad..e27ab9d 100644 --- a/src/R34Sharp/Components/R34BaseApiComponent.cs +++ b/src/R34Sharp/Components/R34BaseApiComponent.cs @@ -1,6 +1,12 @@ -using System.Xml.Serialization; +using R34Sharp.Entities; -namespace R34Sharp +using System; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace R34Sharp.Components { /// /// Component responsible for creating expandable processes for the API. @@ -14,7 +20,7 @@ public abstract class R34BaseApiComponent internal void Build(R34ApiClient client) { - ApiClient = client; + this.ApiClient = client; } /// @@ -24,21 +30,36 @@ internal void Build(R34ApiClient client) /// Url where the request will be made. /// XML serializer that will be used for conversion operations. /// Generic data filled with requested data. + /// + /// + /// protected async Task GetAsync(string url, XmlSerializer serializer) where T : R34Data { T result = default(T); try { - HttpRequestMessage message = new(HttpMethod.Get, url); - HttpResponseMessage msg = await ApiClient.Client.SendAsync(message); + using HttpRequestMessage message = new(HttpMethod.Get, url); + using HttpResponseMessage msg = await this.ApiClient.Client.SendAsync(message); - msg.EnsureSuccessStatusCode(); + _ = msg.EnsureSuccessStatusCode(); - result = await Task.Run(async () => (T)serializer.Deserialize(new StringReader(await msg.Content.ReadAsStringAsync()))); - await result.BuildAsync(ApiClient); + string content = await msg.Content.ReadAsStringAsync(); + result = (T)serializer.Deserialize(new StringReader(content)); + await result.BuildAsync(this.ApiClient); + } + catch (HttpRequestException ex) + { + throw new HttpRequestException("Error while making HTTP request.", ex); + } + catch (InvalidOperationException ex) + { + throw new InvalidOperationException("Invalid operation while processing the response.", ex); + } + catch (Exception ex) + { + throw new Exception("An unexpected error occurred while processing the response.", ex); } - catch (Exception) { } return await Task.FromResult(result); } diff --git a/src/R34Sharp/Components/R34CommentsComponent.cs b/src/R34Sharp/Components/R34CommentsComponent.cs index 68cea74..ba5693a 100644 --- a/src/R34Sharp/Components/R34CommentsComponent.cs +++ b/src/R34Sharp/Components/R34CommentsComponent.cs @@ -1,13 +1,19 @@ -using System.Xml.Serialization; +using R34Sharp.Entities.Comments; +using R34Sharp.Net; +using R34Sharp.Search; +using R34Sharp.Url; -namespace R34Sharp +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace R34Sharp.Components { /// /// API component responsible for processes involving Rule34 comments chains. /// public sealed class R34CommentsComponent : R34BaseApiComponent { - private static readonly XmlSerializer _commentsXmlSerializer = new(typeof(R34Comments)); + private readonly XmlSerializer _commentsXmlSerializer = new(typeof(R34Comments)); /// /// Get comments for given Rule34 post based on a . @@ -24,7 +30,7 @@ public async Task GetCommentsAsync(R34CommentsSearchBuilder searchB urlBuilder.AddParameter("post_id", searchBuilder.PostId.ToString()); // Get Result - return await GetAsync(urlBuilder.Build(), _commentsXmlSerializer); + return await GetAsync(urlBuilder.Build(), this._commentsXmlSerializer); } } } diff --git a/src/R34Sharp/Components/R34PostsComponent.cs b/src/R34Sharp/Components/R34PostsComponent.cs index 2564f8a..8d706d7 100644 --- a/src/R34Sharp/Components/R34PostsComponent.cs +++ b/src/R34Sharp/Components/R34PostsComponent.cs @@ -1,41 +1,58 @@ -using System.Xml.Serialization; +using R34Sharp.Entities.Posts; +using R34Sharp.Net; +using R34Sharp.Search; +using R34Sharp.Url; -namespace R34Sharp +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace R34Sharp.Components { /// /// API component responsible for processes involving Rule34 post chains. /// public sealed class R34PostsComponent : R34BaseApiComponent { - private static readonly XmlSerializer _postsXmlSerializer = new(typeof(R34Posts)); + private readonly XmlSerializer _postsXmlSerializer = new(typeof(R34Posts)); /// /// Get Rule34 posts based on a . /// /// Search builder for Rule34 Posts. /// A collection of Rule34 posts. - /// /// public async Task GetPostsAsync(R34PostsSearchBuilder searchBuilder) { - // Handler Exceptions - if (searchBuilder.Limit < 1 || searchBuilder.Limit > 1000) await Task.FromException(new IndexOutOfRangeException("The limit allowed for obtaining Posts is a value between 1 and 1000.")); - if (searchBuilder.Tags == null || !searchBuilder.Tags.Any()) await Task.FromException(new ArgumentException("Search tags are missing.")); - - // Build Url UrlBuilder urlBuilder = new(R34Endpoints.INDEX); urlBuilder.AddParameter("page", "dapi"); urlBuilder.AddParameter("s", "post"); urlBuilder.AddParameter("q", "index"); urlBuilder.AddParameter("limit", searchBuilder.Limit.ToString()); - urlBuilder.AddParameter("tags", searchBuilder.GetTagsString()); - if (searchBuilder.Offset.HasValue) urlBuilder.AddParameter("pid", searchBuilder.Offset.Value.ToString()); - if (searchBuilder.Id.HasValue) urlBuilder.AddParameter("id", searchBuilder.Id.Value.ToString()); + if (searchBuilder.Tags.Length > 0) + { + urlBuilder.AddParameter("tags", searchBuilder.GetTagsString()); + } + + if (searchBuilder.Offset.HasValue) + { + urlBuilder.AddParameter("pid", searchBuilder.Offset.Value.ToString()); + } + + if (searchBuilder.Id.HasValue) + { + urlBuilder.AddParameter("id", searchBuilder.Id.Value.ToString()); + } + + R34Posts postsResult = await GetAsync(urlBuilder.Build(), this._postsXmlSerializer).ConfigureAwait(false); - // Get Result - R34Posts postsResult = await GetAsync(urlBuilder.Build(), _postsXmlSerializer); - if (searchBuilder.BlockedTags.HasValue) postsResult.Data = postsResult.Data.Where(x => !x.HasTags(searchBuilder.BlockedTags.Value)).ToArray(); + if (searchBuilder.BlockedTags.Length > 0) + { + postsResult.Data = postsResult.Data.Where(x => !x.HasTags(searchBuilder.BlockedTags)).ToArray(); + } return postsResult; } @@ -45,7 +62,7 @@ public async Task GetPostsAsync(R34PostsSearchBuilder searchBuilder) /// /// /// This method performs multiple searches until it finds all posts (specified in the search limit) or until there is no more content. Depending on the types of conditions selected the method can introduce significant delay and result in a possible TimeOut, so use it wisely.

- /// If your filter is very trivial, it is recommended to use + /// If your filter is very trivial, it is recommended to use . ///
/// Search builder for Rule34 Posts. /// The filter that will be applied to each request result. @@ -54,33 +71,36 @@ public async Task GetPostsAsync(R34PostsSearchBuilder searchBuilder) /// public async Task GetPostsByFilterAsync(R34PostsSearchBuilder searchBuilder, Func filter) { - // Handler Exceptions - if (searchBuilder.Limit < 1 || searchBuilder.Limit > 1000) await Task.FromException(new IndexOutOfRangeException("The limit allowed for obtaining Posts is a value between 1 and 1000.")); - if (searchBuilder.Tags == null || !searchBuilder.Tags.Any()) await Task.FromException(new ArgumentException("Search tags are missing.")); - - // Posts List foundPosts = new(); - int currentChunk = 0; // Requests + int currentChunk = 0; + int currentOffset = 0; + while (true) { - searchBuilder.Offset = new(searchBuilder.Offset.Value + currentChunk); - R34Posts posts = await GetPostsAsync(searchBuilder); + currentOffset += currentChunk; + R34Posts posts = await GetPostsAsync(searchBuilder).ConfigureAwait(false); - if (posts == null || posts.Data == null || posts.Count == 0) break; + if (posts == null || posts.Data == null || posts.Count == 0) + { + break; + } foundPosts.AddRange(posts.Data.Where(filter)); - if (foundPosts.Count >= searchBuilder.Limit) break; + if (foundPosts.Count >= searchBuilder.Limit) + { + break; + } currentChunk++; } // Return - return new() + return new R34Posts { Data = foundPosts.Take(searchBuilder.Limit).ToArray(), - Offset = (searchBuilder.Offset.Value + currentChunk) / 2, + Offset = (currentOffset + currentChunk) / 2, }; } } diff --git a/src/R34Sharp/Components/R34TagsComponent.cs b/src/R34Sharp/Components/R34TagsComponent.cs index f6f3aa9..ec0e001 100644 --- a/src/R34Sharp/Components/R34TagsComponent.cs +++ b/src/R34Sharp/Components/R34TagsComponent.cs @@ -1,26 +1,30 @@ -using System.Xml.Serialization; +using R34Sharp.Entities.Tags; +using R34Sharp.Enums; +using R34Sharp.Net; +using R34Sharp.Search; +using R34Sharp.Url; -namespace R34Sharp +using System; +using System.Threading.Tasks; +using System.Xml.Serialization; + +namespace R34Sharp.Components { /// /// API component responsible for processes involving Rule34 tags chains. /// public sealed class R34TagsComponent : R34BaseApiComponent { - private static readonly XmlSerializer _tagsXmlSerializer = new(typeof(R34Tags)); + private readonly XmlSerializer _tagsXmlSerializer = new(typeof(R34Tags)); /// /// Get a list of Rule34 tags based on a . /// /// Create a custom search to get tags and their information from Rule34. /// A collection of Rule34 tags. - /// + /// public async Task GetTagsAsync(R34TagsSearchBuilder searchBuilder) { - // Handler Exceptions - if (searchBuilder.Limit < 1 || searchBuilder.Limit > 100) await Task.FromException(new IndexOutOfRangeException("The limit allowed for obtaining Tags is a value between 1 and 100.")); - - // Build Url UrlBuilder urlBuilder = new(R34Endpoints.INDEX); urlBuilder.AddParameter("page", "dapi"); urlBuilder.AddParameter("s", "tag"); @@ -29,14 +33,21 @@ public async Task GetTagsAsync(R34TagsSearchBuilder searchBuilder) switch (searchBuilder.SearchType) { - case R34TagSearchType.Name: urlBuilder.AddParameter("name", searchBuilder.Search); break; - case R34TagSearchType.Pattern: urlBuilder.AddParameter("name_pattern", searchBuilder.Search); break; - case R34TagSearchType.Id: urlBuilder.AddParameter("id", searchBuilder.Search); break; - default: urlBuilder.AddParameter("name", searchBuilder.Search); break; + case R34TagSearchType.Name: + urlBuilder.AddParameter("name", searchBuilder.Search); + break; + case R34TagSearchType.Pattern: + urlBuilder.AddParameter("name_pattern", searchBuilder.Search); + break; + case R34TagSearchType.Id: + urlBuilder.AddParameter("id", searchBuilder.Search); + break; + default: + urlBuilder.AddParameter("name", searchBuilder.Search); + break; } - // Get Result - return await GetAsync(urlBuilder.Build(), _tagsXmlSerializer); + return await GetAsync(urlBuilder.Build(), this._tagsXmlSerializer); } } } diff --git a/src/R34Sharp/Helpers/DateTimeHelpers.cs b/src/R34Sharp/Date/DateTimeParser.cs similarity index 70% rename from src/R34Sharp/Helpers/DateTimeHelpers.cs rename to src/R34Sharp/Date/DateTimeParser.cs index 29512d1..d908243 100644 --- a/src/R34Sharp/Helpers/DateTimeHelpers.cs +++ b/src/R34Sharp/Date/DateTimeParser.cs @@ -1,8 +1,9 @@ -using System.Globalization; +using System; +using System.Globalization; -namespace R34Sharp +namespace R34Sharp.Date { - internal static class DateTimeHelpers + internal static class DateTimeParser { internal static DateTime R34Parse(string datetimeString, string format) { diff --git a/src/R34Sharp/Entities/Comments/R34Comment.cs b/src/R34Sharp/Entities/Comments/R34Comment.cs index 7993a61..131b064 100644 --- a/src/R34Sharp/Entities/Comments/R34Comment.cs +++ b/src/R34Sharp/Entities/Comments/R34Comment.cs @@ -1,7 +1,11 @@ -using System.Xml; +using R34Sharp.Date; + +using System; +using System.Threading.Tasks; +using System.Xml; using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Comments { /// /// A Rule34 post comment. @@ -9,12 +13,6 @@ namespace R34Sharp [XmlRoot(ElementName = "comment")] public sealed class R34Comment : R34Entity { - #region HEADER - /// - /// Date and time the comment was published. - /// - [XmlIgnore] public DateTime CreatedAt { get; private set; } - #endregion #region FILE /// /// The ID of comment. @@ -47,11 +45,9 @@ public sealed class R34Comment : R34Entity [XmlAttribute(AttributeName = "body")] public string Content { get; set; } #endregion - /// - /// + /// protected override async Task OnBuildAsync() { - CreatedAt = DateTimeHelpers.R34Parse(CreatedAtString, "yyyy-dd-MM mm:ss"); await Task.CompletedTask; } } diff --git a/src/R34Sharp/Entities/Comments/R34Comments.cs b/src/R34Sharp/Entities/Comments/R34Comments.cs index d790381..fc4c44d 100644 --- a/src/R34Sharp/Entities/Comments/R34Comments.cs +++ b/src/R34Sharp/Entities/Comments/R34Comments.cs @@ -1,7 +1,10 @@ -using System.Xml; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Comments { /// /// Represents a collection of Rule34 Comments. @@ -22,26 +25,19 @@ public sealed class R34Comments : R34Data /// /// The count of comments present in this collection. /// - [XmlIgnore] public ulong Count => Data == null ? 0 : (ulong)Data.Length; + [XmlIgnore] public ulong Count => this.Data == null ? 0 : (ulong)this.Data.Length; internal override async Task BuildAsync(R34ApiClient instance) { - try + if (this.Data == null) { - if (Data == null) - return; - - await Parallel.ForEachAsync(Data, new Func(async (current, token) => - { - await current.BuildAsync(instance); - })); - } - catch (Exception e) - { - await Task.FromException(e); + return; } - await Task.CompletedTask; + await Parallel.ForEachAsync(this.Data, new Func(async (current, token) => + { + await current.BuildAsync(instance); + })); } } } diff --git a/src/R34Sharp/Entities/Posts/R34Post.cs b/src/R34Sharp/Entities/Posts/R34Post.cs index a24f326..7b6fff9 100644 --- a/src/R34Sharp/Entities/Posts/R34Post.cs +++ b/src/R34Sharp/Entities/Posts/R34Post.cs @@ -1,12 +1,18 @@ -using System.Collections.Concurrent; -using System.ComponentModel; +using R34Sharp.Date; +using R34Sharp.Entities.Comments; +using R34Sharp.Enums; +using R34Sharp.IO; +using R34Sharp.Models; + +using System; +using System.Collections.Concurrent; +using System.IO; using System.Numerics; +using System.Threading.Tasks; using System.Xml; -using System.Xml.Linq; -using System.Xml.Schema; using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Posts { /// /// A Rule34 post. @@ -149,6 +155,7 @@ public sealed class R34Post : R34Entity /// [XmlAttribute(AttributeName = "status")] public string Status { get; set; } + /// protected override async Task OnBuildAsync() { await SetFilesInfosAsync(); @@ -158,50 +165,49 @@ protected override async Task OnBuildAsync() } private async Task SetFilesInfosAsync() { - FileName = Path.GetFileNameWithoutExtension(FileUrl); - FileExtension = Path.GetExtension(FileUrl); - FileType = FileHelpers.GetMediaType(FileExtension); + this.FileName = Path.GetFileNameWithoutExtension(this.FileUrl); + this.FileExtension = Path.GetExtension(this.FileUrl); + this.FileType = MediaFileFormatDetector.GetMediaType(this.FileExtension); await Task.CompletedTask; } private async Task SetInfosAsync() { // CREATE TIMESTAMP - CreatedAt = DateTimeHelpers.R34Parse(CreatedAtString, "ddd MMM dd HH:mm:ss zzz yyyy"); + this.CreatedAt = DateTimeParser.R34Parse(this.CreatedAtString, "ddd MMM dd HH:mm:ss zzz yyyy"); // DIMENSIONS - FileDimensions = new(Width, Height); - PreviewFileDimensions = new(PreviewWidth, PreviewHeight); + this.FileDimensions = new(this.Width, this.Height); + this.PreviewFileDimensions = new(this.PreviewWidth, this.PreviewHeight); // RATING - switch (RatingString) + this.Rating = this.RatingString switch { - case "g": Rating = R34Rating.General; break; - case "s": Rating = R34Rating.Safe; break; - case "q": Rating = R34Rating.Questionable; break; - case "e": Rating = R34Rating.Explicit; break; - default: Rating = R34Rating.General; break; - } - + "g" => R34Rating.General, + "s" => R34Rating.Safe, + "q" => R34Rating.Questionable, + "e" => R34Rating.Explicit, + _ => R34Rating.General, + }; await Task.CompletedTask; } /// - /// Return all existing tags in the post as objects. + /// Return all existing tags in the post as objects. /// /// /// Collection of post tags. /// - public async Task> GetTagsAsync() + public async Task GetTagsAsync() { - string[] tagsArray = TagsString.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - ConcurrentBag tags = new(); + string[] tagsArray = this.TagsString.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + ConcurrentBag tags = new(); await Parallel.ForEachAsync(tagsArray, async (item, token) => { await Task.Yield(); - R34TagModel tag = new(item); + R34FormattedTag tag = new(item); tags.Add(tag); }); @@ -213,9 +219,9 @@ await Parallel.ForEachAsync(tagsArray, async (item, token) => /// /// The tag to fetch. /// True if the Tag is found. - public bool HasTag(R34TagModel tag) + public bool HasTag(R34FormattedTag tag) { - return Array.Find(TagsString.Split(' ', StringSplitOptions.RemoveEmptyEntries), x => x == tag.Name) != null; + return Array.Find(this.TagsString.Split(' ', StringSplitOptions.RemoveEmptyEntries), x => x == tag.Name) != null; } /// @@ -223,12 +229,14 @@ public bool HasTag(R34TagModel tag) /// /// The tags to be fetch. /// True if all Tags are found. - public bool HasTags(IEnumerable tags) + public bool HasTags(R34FormattedTag[] tags) { - foreach (R34TagModel tag in tags) + for (int i = 0; i < tags.Length; i++) { - if (!HasTag(tag)) + if (!HasTag(tags[i])) + { return false; + } } return true; @@ -241,10 +249,7 @@ public bool HasTags(IEnumerable tags) /// public async Task GetCommentsAsync() { - if (!HasComments) - return null; - - return await Task.FromResult(await R34Client.Comments.GetCommentsAsync(new() { PostId = Id })); + return !this.HasComments ? null : await Task.FromResult(await this.R34Client.Comments.GetCommentsAsync(new() { PostId = this.Id })); } /// @@ -253,7 +258,7 @@ public async Task GetCommentsAsync() /// A containing the post file. public async Task DownloadFileAsync() { - return await Task.FromResult(await DownloadAsync(FileUrl)); + return await Task.FromResult(await DownloadAsync(this.FileUrl)); } /// @@ -262,7 +267,7 @@ public async Task DownloadFileAsync() /// A containing the post preview file. public async Task DownloadFilePreviewAsync() { - return await Task.FromResult(await DownloadAsync(PreviewUrl)); + return await Task.FromResult(await DownloadAsync(this.PreviewUrl)); } private async Task DownloadAsync(string url) @@ -270,10 +275,10 @@ private async Task DownloadAsync(string url) MemoryStream ms = new(); // Get Stream - using Stream fileStream = await R34Client.Client.GetStreamAsync(url); + using Stream fileStream = await this.R34Client.Client.GetStreamAsync(url); await fileStream.CopyToAsync(ms); - // Return Stream + // Return Stream return ms; } } diff --git a/src/R34Sharp/Entities/Posts/R34Posts.cs b/src/R34Sharp/Entities/Posts/R34Posts.cs index b69854b..89adef7 100644 --- a/src/R34Sharp/Entities/Posts/R34Posts.cs +++ b/src/R34Sharp/Entities/Posts/R34Posts.cs @@ -1,8 +1,10 @@ -using System.Xml; -using System.Xml.Schema; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Posts { /// /// Represents a collection of Rule34 posts. @@ -18,7 +20,7 @@ public sealed class R34Posts : R34Data /// /// The count of posts present in this collection. /// - [XmlIgnore] public ulong Count => Data == null ? 0 : (ulong)Data.Length; + [XmlIgnore] public ulong Count => this.Data == null ? 0 : (ulong)this.Data.Length; /// /// The offset of current collection. @@ -27,22 +29,15 @@ public sealed class R34Posts : R34Data internal override async Task BuildAsync(R34ApiClient instance) { - try + if (this.Data == null) { - if (Data == null) - return; - - await Parallel.ForEachAsync(Data, new Func(async (current, token) => - { - await current.BuildAsync(instance); - })); - } - catch (Exception e) - { - await Task.FromException(e); + return; } - await Task.CompletedTask; + await Parallel.ForEachAsync(this.Data, new Func(async (current, token) => + { + await current.BuildAsync(instance); + })); } } } diff --git a/src/R34Sharp/Entities/R34Data.cs b/src/R34Sharp/Entities/R34Data.cs index d877e01..58e6766 100644 --- a/src/R34Sharp/Entities/R34Data.cs +++ b/src/R34Sharp/Entities/R34Data.cs @@ -1,4 +1,6 @@ -namespace R34Sharp +using System.Threading.Tasks; + +namespace R34Sharp.Entities { /// /// Represents a generic data collection of R34Sharp entities. diff --git a/src/R34Sharp/Entities/R34Entity.cs b/src/R34Sharp/Entities/R34Entity.cs index e408b22..562ba75 100644 --- a/src/R34Sharp/Entities/R34Entity.cs +++ b/src/R34Sharp/Entities/R34Entity.cs @@ -1,4 +1,6 @@ -namespace R34Sharp +using System.Threading.Tasks; + +namespace R34Sharp.Entities { /// /// Represents an R34Sharp entity. @@ -12,7 +14,7 @@ public abstract class R34Entity internal async Task BuildAsync(R34ApiClient instance) { - R34Client = instance; + this.R34Client = instance; await OnBuildAsync(); } diff --git a/src/R34Sharp/Entities/Tags/R34Tag.cs b/src/R34Sharp/Entities/Tags/R34Tag.cs index 835a030..3c499bc 100644 --- a/src/R34Sharp/Entities/Tags/R34Tag.cs +++ b/src/R34Sharp/Entities/Tags/R34Tag.cs @@ -1,6 +1,7 @@ -using System.Xml.Serialization; +using System.Threading.Tasks; +using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Tags { /// /// A Rule34 tag. @@ -35,8 +36,7 @@ public sealed class R34Tag : R34Entity [XmlAttribute(AttributeName = "count")] public int PostsCount { get; set; } #endregion - /// - /// + /// protected override async Task OnBuildAsync() { await Task.CompletedTask; diff --git a/src/R34Sharp/Entities/Tags/R34Tags.cs b/src/R34Sharp/Entities/Tags/R34Tags.cs index 56691e0..380dbef 100644 --- a/src/R34Sharp/Entities/Tags/R34Tags.cs +++ b/src/R34Sharp/Entities/Tags/R34Tags.cs @@ -1,7 +1,10 @@ -using System.Xml; +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; using System.Xml.Serialization; -namespace R34Sharp +namespace R34Sharp.Entities.Tags { /// /// Represents a collection of Rule34 tags. @@ -22,26 +25,19 @@ public sealed class R34Tags : R34Data /// /// The count of comments present in this collection. /// - [XmlIgnore] public ulong Count => Data == null ? 0 : (ulong)Data.Length; + [XmlIgnore] public ulong Count => this.Data == null ? 0 : (ulong)this.Data.Length; internal override async Task BuildAsync(R34ApiClient instance) { - try + if (this.Data == null) { - if (Data == null) - return; - - await Parallel.ForEachAsync(Data, new Func(async (current, token) => - { - await current.BuildAsync(instance); - })); - } - catch (Exception e) - { - await Task.FromException(e); + return; } - await Task.CompletedTask; + await Parallel.ForEachAsync(this.Data, new Func(async (current, token) => + { + await current.BuildAsync(instance); + })); } } } diff --git a/src/R34Sharp/Enums/R34PostEnums.cs b/src/R34Sharp/Enums/R34PostEnums.cs index f6fa87d..9281b3b 100644 --- a/src/R34Sharp/Enums/R34PostEnums.cs +++ b/src/R34Sharp/Enums/R34PostEnums.cs @@ -1,4 +1,4 @@ -namespace R34Sharp +namespace R34Sharp.Enums { /// /// Media type of a file. diff --git a/src/R34Sharp/Enums/R34SearchEnums.cs b/src/R34Sharp/Enums/R34SearchEnums.cs index 822d63d..8604d44 100644 --- a/src/R34Sharp/Enums/R34SearchEnums.cs +++ b/src/R34Sharp/Enums/R34SearchEnums.cs @@ -1,4 +1,4 @@ -namespace R34Sharp +namespace R34Sharp.Enums { /// /// Search type by Rule34 Tags. diff --git a/src/R34Sharp/Helpers/FileHelpers.cs b/src/R34Sharp/IO/MediaFileFormatDetector.cs similarity index 86% rename from src/R34Sharp/Helpers/FileHelpers.cs rename to src/R34Sharp/IO/MediaFileFormatDetector.cs index d7b8bc5..98c2af6 100644 --- a/src/R34Sharp/Helpers/FileHelpers.cs +++ b/src/R34Sharp/IO/MediaFileFormatDetector.cs @@ -1,6 +1,10 @@ -namespace R34Sharp +using R34Sharp.Enums; + +using System; + +namespace R34Sharp.IO { - internal static class FileHelpers + internal static class MediaFileFormatDetector { private static readonly string[] ImageExtensions = { diff --git a/src/R34Sharp/Models/R34FormattedTag.cs b/src/R34Sharp/Models/R34FormattedTag.cs new file mode 100644 index 0000000..8db9b61 --- /dev/null +++ b/src/R34Sharp/Models/R34FormattedTag.cs @@ -0,0 +1,37 @@ +namespace R34Sharp.Models +{ + /// + /// Represents a Rule34-formatted tag. + /// + public sealed class R34FormattedTag + { + /// + /// Gets or sets the current tag name formatted for the Rule34 tag template. + /// + public string Name + { + get => this.name; + set => this.name = value; + } + + private string name; + + /// + /// Initializes a new instance of the class. + /// + /// The name of the Rule34 Tag. + public R34FormattedTag(string value) + { + this.name = value.Trim().Replace(' ', '_').ToLower(); + } + + /// + /// Returns the current Tag name. + /// + /// Tag Name. + public override string ToString() + { + return this.name; + } + } +} \ No newline at end of file diff --git a/src/R34Sharp/Models/R34TagModel.cs b/src/R34Sharp/Models/R34TagModel.cs deleted file mode 100644 index 8434bb1..0000000 --- a/src/R34Sharp/Models/R34TagModel.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace R34Sharp -{ - /// - /// The Rule34 model Tag Builder. - /// - public sealed class R34TagModel - { - /// - /// Current tag name formatted for the Rule34 tag template. - /// - public string Name - { - get => name; - set => name = value; - } - private string name; - - /// - /// Build a Tag that is automatically formatted for the Rule34 style. - /// - /// - /// All spaces are automatically replaced by the "_" character and the string is fully formatted for lowercase. - /// - /// The name of the Rule34 Tag. - public R34TagModel(string value) - { - name = value.Trim().Replace(' ', '_').ToLower(); - } - - /// - /// Returns the current Tag name. - /// - /// Tag Name. - public override string ToString() - { - return name; - } - } -} \ No newline at end of file diff --git a/src/R34Sharp/Net/R34Endpoints.cs b/src/R34Sharp/Net/R34Endpoints.cs index fffe0d8..1c55917 100644 --- a/src/R34Sharp/Net/R34Endpoints.cs +++ b/src/R34Sharp/Net/R34Endpoints.cs @@ -1,4 +1,4 @@ -namespace R34Sharp +namespace R34Sharp.Net { internal static class R34Endpoints { diff --git a/src/R34Sharp/R34ApiClient.cs b/src/R34Sharp/R34ApiClient.cs index 74f4740..189340d 100644 --- a/src/R34Sharp/R34ApiClient.cs +++ b/src/R34Sharp/R34ApiClient.cs @@ -1,5 +1,9 @@ -using System.Xml.Serialization; +using R34Sharp.Components; +using R34Sharp.Net; + +using System; using System.Net; +using System.Net.Http; namespace R34Sharp { @@ -16,7 +20,7 @@ public sealed class R34ApiClient : IDisposable /// /// Checks if this class has already been Disposable. /// - public bool Disposed { get; private set; } + public bool DisposedValue => this.disposedValue; /// /// API component responsible for posts. @@ -33,46 +37,58 @@ public sealed class R34ApiClient : IDisposable /// public R34CommentsComponent Comments { get; private set; } + private bool disposedValue; + /// /// Initializes the Rule34 Wrapper to enable communications between the website and the client. /// public R34ApiClient() { - StartClient(); + BuildClient(); - Posts = new(); - Tags = new(); - Comments = new(); + this.Posts = new(); + this.Tags = new(); + this.Comments = new(); - Posts.Build(this); - Tags.Build(this); - Comments.Build(this); + this.Posts.Build(this); + this.Tags.Build(this); + this.Comments.Build(this); } - private void StartClient() + private void BuildClient() { HttpClientHandler handler = new() { UseCookies = false, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate, + MaxConnectionsPerServer = 10, }; - Client = new(handler) + this.Client = new(handler) { BaseAddress = new(R34Endpoints.BASE_URI), + Timeout = TimeSpan.FromSeconds(30), }; - Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 R34Sharp"); + this.Client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 R34Sharp"); } - /// - /// Dispose and initiate API client shutdown processes. In addition to relieving memory processes. - /// - public void Dispose() + private void Dispose(bool disposing) { - Client.Dispose(); - Disposed = true; + if (!this.disposedValue) + { + if (disposing) + { + ((IDisposable)this.Client).Dispose(); + } + + this.disposedValue = true; + } + } + void IDisposable.Dispose() + { + Dispose(disposing: true); GC.SuppressFinalize(this); } } diff --git a/src/R34Sharp/R34Sharp.csproj b/src/R34Sharp/R34Sharp.csproj index c88c4a7..c758fff 100644 --- a/src/R34Sharp/R34Sharp.csproj +++ b/src/R34Sharp/R34Sharp.csproj @@ -1,14 +1,20 @@ - - - - + + + + - - Library - + + Library + true + - - An unofficial .NET Core wrapper for the Rule34 API. - - + + An unofficial .NET Core wrapper for the Rule34 API. + General changes and reorganizations of the project, aiming at performance and process optimization. + True + README.md + True + True + LICENSE.txt + \ No newline at end of file diff --git a/src/R34Sharp/Search/R34CommentsSearchBuilder.cs b/src/R34Sharp/Search/R34CommentsSearchBuilder.cs index 94c9c22..10ff003 100644 --- a/src/R34Sharp/Search/R34CommentsSearchBuilder.cs +++ b/src/R34Sharp/Search/R34CommentsSearchBuilder.cs @@ -1,9 +1,9 @@ -namespace R34Sharp +namespace R34Sharp.Search { /// /// A search builder for Rule34 post comments. /// - public class R34CommentsSearchBuilder + public sealed class R34CommentsSearchBuilder { /// /// The ID of the post. @@ -15,7 +15,7 @@ public class R34CommentsSearchBuilder /// public R34CommentsSearchBuilder() { - WithId(0); + _ = WithId(0); } /// @@ -25,7 +25,7 @@ public R34CommentsSearchBuilder() /// This search builder. public R34CommentsSearchBuilder WithId(ulong value) { - PostId = value; + this.PostId = value; return this; } } diff --git a/src/R34Sharp/Search/R34PostsSearchBuilder.cs b/src/R34Sharp/Search/R34PostsSearchBuilder.cs index 7330cc5..3a0d5b7 100644 --- a/src/R34Sharp/Search/R34PostsSearchBuilder.cs +++ b/src/R34Sharp/Search/R34PostsSearchBuilder.cs @@ -1,11 +1,15 @@ -using System.Text; +using R34Sharp.Models; +using R34Sharp.Tools; -namespace R34Sharp +using System; +using System.Linq; + +namespace R34Sharp.Search { /// /// A search builder for Rule34 Posts. /// - public class R34PostsSearchBuilder + public sealed class R34PostsSearchBuilder { /// /// The limit of posts the API should return. @@ -13,7 +17,19 @@ public class R34PostsSearchBuilder /// /// The value must be between 1 and 1000 posts. /// - public int Limit { get; set; } + public int Limit + { + get => this._limit; + set + { + if (value < 1 || value > 1000) + { + throw new ArgumentOutOfRangeException(nameof(value), "The limit must be between 1 and 1000."); + } + + this._limit = value; + } + } /// /// The Id of a specific Rule34 post. @@ -28,40 +44,44 @@ public class R34PostsSearchBuilder /// /// /// To find a specific set of posts in a search, you can use "offsets" to find the posts following the number of offsets already searched. For example, if you've searched the last 1000 posts and want to get the next posts without overloading the search, you can set the number of "offsets" to "1" and so on. It's important to remember that "offsets" are directly related to the number of posts that will be searched.

- /// If this value is filled in, pay attention to the of the search, as there may be inconsistency. + /// If this value is filled in, pay attention to the of the search, as there may be inconsistency. ///
public Optional Offset { get; set; } /// /// The tags that will be used for the search. /// - public IEnumerable Tags { get; set; } + public R34FormattedTag[] Tags { get => this._tags; set => this._tags = value; } /// /// The tags that will be ignored when searching for Posts. /// - public Optional> BlockedTags { get; set; } + public R34FormattedTag[] BlockedTags { get => this._blockedTags; set => this._blockedTags = value; } + + private int _limit; + private R34FormattedTag[] _tags; + private R34FormattedTag[] _blockedTags; /// /// Build a custom search for Rule34 Posts. /// public R34PostsSearchBuilder() { - WithLimit(100); - WithTags(Array.Empty()); + _ = WithLimit(100); + _ = WithTags(Array.Empty()); + _ = WithBlockedTags(Array.Empty()); - BlockedTags = new(); - Id = new(); - Offset = new(); + this.Id = new(); + this.Offset = new(); } internal string GetTagsString() { - return ConvertTagsToString(Tags); + return ConvertTagsToString(this._tags); } internal string GetBlockedTagsString() { - return ConvertTagsToString(BlockedTags.Value); + return ConvertTagsToString(this._blockedTags); } /// @@ -71,7 +91,7 @@ internal string GetBlockedTagsString() /// This search builder. public R34PostsSearchBuilder WithLimit(int value) { - Limit = value; + this.Limit = value; return this; } @@ -82,7 +102,7 @@ public R34PostsSearchBuilder WithLimit(int value) /// This search builder. public R34PostsSearchBuilder WithId(ulong value) { - Id = new(value); + this.Id = new(value); return this; } @@ -93,7 +113,7 @@ public R34PostsSearchBuilder WithId(ulong value) /// This search builder. public R34PostsSearchBuilder WithOffset(int value) { - Offset = new(value); + this.Offset = new(value); return this; } @@ -102,9 +122,9 @@ public R34PostsSearchBuilder WithOffset(int value) /// /// The tags collection. /// This search builder. - public R34PostsSearchBuilder WithTags(IEnumerable tags) + public R34PostsSearchBuilder WithTags(R34FormattedTag[] tags) { - Tags = tags; + this.Tags = tags; return this; } @@ -113,30 +133,15 @@ public R34PostsSearchBuilder WithTags(IEnumerable tags) ///
/// The tags collection. /// This search builder. - public R34PostsSearchBuilder WithBlockedTags(IEnumerable tags) + public R34PostsSearchBuilder WithBlockedTags(R34FormattedTag[] tags) { - BlockedTags = new(tags); + this.BlockedTags = tags; return this; } - private static string ConvertTagsToString(IEnumerable tags) + private static string ConvertTagsToString(R34FormattedTag[] tags) { - int length = tags.Count(); - - StringBuilder tagsString = new(); - for (int i = 0; i < length; i++) - { - R34TagModel tag = tags.ElementAtOrDefault(i); - if (tag == null) continue; - - _ = tagsString.Append(tag.Name); - if (i < length - 1) - { - _ = tagsString.Append('+'); - } - } - - return tagsString.ToString(); + return string.Join('+', tags.Where(tag => tag != null).Select(tag => tag.Name)); } } } diff --git a/src/R34Sharp/Search/R34TagsSearchBuilder.cs b/src/R34Sharp/Search/R34TagsSearchBuilder.cs index f4f363d..e39ee80 100644 --- a/src/R34Sharp/Search/R34TagsSearchBuilder.cs +++ b/src/R34Sharp/Search/R34TagsSearchBuilder.cs @@ -1,9 +1,13 @@ -namespace R34Sharp +using R34Sharp.Enums; + +using System; + +namespace R34Sharp.Search { /// /// A search builder for Rule34 Tags. /// - public class R34TagsSearchBuilder + public sealed class R34TagsSearchBuilder { /// /// The type of search that will be performed on this browser. @@ -21,16 +25,30 @@ public class R34TagsSearchBuilder /// /// The value must be between 1 and 100. /// - public int Limit { get; set; } + public int Limit + { + get => this._limit; + set + { + if (value < 1 || value > 100) + { + throw new ArgumentOutOfRangeException(nameof(value), "The limit must be between 1 and 100."); + } + + this._limit = value; + } + } + + private int _limit; /// /// Build a custom search for Rule34 Tags. /// public R34TagsSearchBuilder() { - WithSearchType(R34TagSearchType.Name); - WithSearch(string.Empty); - WithLimit(100); + _ = WithSearchType(R34TagSearchType.Name); + _ = WithSearch(string.Empty); + _ = WithLimit(100); } /// @@ -40,7 +58,7 @@ public R34TagsSearchBuilder() /// This search builder. public R34TagsSearchBuilder WithSearchType(R34TagSearchType value) { - SearchType = value; + this.SearchType = value; return this; } @@ -51,7 +69,7 @@ public R34TagsSearchBuilder WithSearchType(R34TagSearchType value) /// This search builder. public R34TagsSearchBuilder WithSearch(string value) { - Search = value; + this.Search = value; return this; } @@ -62,7 +80,7 @@ public R34TagsSearchBuilder WithSearch(string value) /// This search builder. public R34TagsSearchBuilder WithLimit(int value) { - Limit = value; + this.Limit = value; return this; } } diff --git a/src/R34Sharp/Utilities/Optional.cs b/src/R34Sharp/Tools/Optional.cs similarity index 87% rename from src/R34Sharp/Utilities/Optional.cs rename to src/R34Sharp/Tools/Optional.cs index f482164..9512086 100644 --- a/src/R34Sharp/Utilities/Optional.cs +++ b/src/R34Sharp/Tools/Optional.cs @@ -1,4 +1,4 @@ -namespace R34Sharp +namespace R34Sharp.Tools { /// /// An optional value that can be set or ignored. @@ -21,7 +21,7 @@ public sealed class Optional /// public Optional() { - HasValue = false; + this.HasValue = false; } /// @@ -30,8 +30,8 @@ public Optional() /// The value that the current instance will have. public Optional(T value) { - HasValue = true; - Value = value; + this.HasValue = true; + this.Value = value; } } } \ No newline at end of file diff --git a/src/R34Sharp/Url/UrlBuilder.cs b/src/R34Sharp/Url/UrlBuilder.cs new file mode 100644 index 0000000..4f3b56d --- /dev/null +++ b/src/R34Sharp/Url/UrlBuilder.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Text; + +namespace R34Sharp.Url +{ + internal sealed class UrlBuilder + { + internal string BaseAddress { get; private set; } + private readonly List<(string name, string value)> parameters = new(); + + internal UrlBuilder(string address) + { + this.BaseAddress = address; + } + + internal void AddParameter(string name, string value) + { + this.parameters.Add((name, value)); + } + + internal void Clear() + { + this.parameters.Clear(); + } + + internal string Build() + { + StringBuilder addressBuilder = new(); + _ = addressBuilder.Append($"{this.BaseAddress}?"); + + for (int i = 0; i < this.parameters.Count; i++) + { + (string, string) parameter = this.parameters[i]; + _ = addressBuilder.Append($"{parameter.Item1.ToLower()}={parameter.Item2.ToLower()}"); + + if (i < this.parameters.Count - 1) + { + _ = addressBuilder.Append('&'); + } + } + + return addressBuilder.ToString(); + } + } +} diff --git a/src/R34Sharp/Utilities/Url/UrlBuilder.cs b/src/R34Sharp/Utilities/Url/UrlBuilder.cs deleted file mode 100644 index 6e6b48f..0000000 --- a/src/R34Sharp/Utilities/Url/UrlBuilder.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Text; - -namespace R34Sharp -{ - internal sealed class UrlBuilder - { - internal string BaseAddress { get; private set; } - private readonly Dictionary parameters = new(); - - internal UrlBuilder(string address) - { - BaseAddress = address; - } - - internal void AddParameter(string name, string value) - { - parameters.TryAdd(name, value); - } - - internal string Build() - { - StringBuilder addressBuilder = new(); - addressBuilder.Append($"{BaseAddress}?"); - - for (int i = 0; i < parameters.Count; i++) - { - KeyValuePair parameter = parameters.ElementAt(i); - _ = addressBuilder.Append($"{parameter.Key.ToLower()}={parameter.Value.ToLower()}"); - - if (i < parameters.Count - 1) - _ = addressBuilder.Append('&'); - } - - return addressBuilder.ToString(); - } - } -} diff --git a/src/Targets/Project_Compilation.targets b/src/Targets/Project_Compilation.targets index fbf78d3..bcba3d1 100644 --- a/src/Targets/Project_Compilation.targets +++ b/src/Targets/Project_Compilation.targets @@ -6,15 +6,16 @@ - + ../../src/Output/$(MSBuildProjectName)/$(Plataform)/Debug/ - + + ../../src/Output/$(MSBuildProjectName)/$(Plataform)/Release/ - + IL2026 diff --git a/src/Targets/Project_Infos.targets b/src/Targets/Project_Infos.targets index 624ad55..a8bd08a 100644 --- a/src/Targets/Project_Infos.targets +++ b/src/Targets/Project_Infos.targets @@ -8,7 +8,7 @@ R34Sharp_Icon.png Starciad Starciad - Starciad © 2023 + Starciad © 2024 git api; wrapper; csharp; wrapper-api; nsfw; nsfw-api; rule34api; dotnet; api-wrapper; rule34; dotnet-api; r34; rule34-api; rule34xxx; dotnet-nsfw; dotnet-core; rule34-downloader; en-US @@ -20,14 +20,14 @@ - 1.3.1 + 2.5.0 - + True \ @@ -37,6 +37,9 @@ \ - + + true + \ + \ No newline at end of file diff --git a/src/Targets/Project_Settings.targets b/src/Targets/Project_Settings.targets index 8338f02..04a3c44 100644 --- a/src/Targets/Project_Settings.targets +++ b/src/Targets/Project_Settings.targets @@ -8,7 +8,7 @@ disable - True + disable True \ No newline at end of file