Skip to content

Commit

Permalink
Merge pull request #50 from LucioMacarine/master
Browse files Browse the repository at this point in the history
Haiii :3
  • Loading branch information
Xwilarg authored Aug 26, 2023
2 parents 12e5494 + a1bd371 commit f64dce0
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 25 deletions.
26 changes: 25 additions & 1 deletion BooruSharp/Booru/ABooru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ private protected virtual Search.Tag.SearchResult GetTagSearchResult(object json
private protected virtual Search.Wiki.SearchResult GetWikiSearchResult(object json)
=> throw new FeatureUnavailable();

private protected virtual Search.Autocomplete.SearchResult[] GetAutocompleteResultAsync(object json)
=> throw new FeatureUnavailable();
private protected virtual async Task<IEnumerable> GetTagEnumerableSearchResultAsync(Uri url)
{
if (TagsUseXml)
Expand Down Expand Up @@ -109,6 +111,11 @@ private protected virtual async Task<IEnumerable> GetTagEnumerableSearchResultAs
/// </summary>
public bool HasFavoriteAPI => !_options.HasFlag(BooruOptions.NoFavorite);

/// <summary>
/// Gets whether it is possible to autocomplete searches in this booru.
/// </summary>
public bool HasAutocompleteAPI => !_options.HasFlag(BooruOptions.NoAutocomplete);

/// <summary>
/// Gets whether this booru can't call post functions without search arguments.
/// </summary>
Expand Down Expand Up @@ -198,6 +205,23 @@ protected ABooru(string domain, UrlFormat format, BooruOptions options)

if (HasCommentAPI)
_commentUrl = CreateQueryString(format, "comment");

if (HasAutocompleteAPI)
_autocompleteUrl = format == UrlFormat.IndexPhp
? new Uri(BaseUrl + "autocomplete.php")
: CreateQueryString(format, "autocomplete");
switch (_format)
{
case UrlFormat.IndexPhp:
_autocompleteUrl = new Uri(BaseUrl + "autocomplete.php");
break;
case UrlFormat.Danbooru:
_autocompleteUrl = new Uri(BaseUrl + "tags/autocomplete.json");
break;
default:
_autocompleteUrl = CreateQueryString(_format, "autocomplete"); //this isn't supposed to work.
break;
}
}

private protected Uri CreateQueryString(UrlFormat format, string query, string squery = "index")
Expand Down Expand Up @@ -355,7 +379,7 @@ protected get
public Uri BaseUrl { get; }

private HttpClient _client;
private readonly Uri _imageUrlXml, _imageUrl, _tagUrl, _wikiUrl, _relatedUrl, _commentUrl; // URLs for differents endpoints
private readonly Uri _imageUrlXml, _imageUrl, _tagUrl, _wikiUrl, _relatedUrl, _commentUrl, _autocompleteUrl; // 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
Expand Down
5 changes: 5 additions & 0 deletions BooruSharp/Booru/BooruOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,10 @@ public enum BooruOptions
/// Indicates that search parameter must be provided in order for post search functions to work.
/// </summary>
NoEmptyPostSearch = 1 << 15,

/// <summary>
/// Indicates that the search autocomplete API is not avaliable.
/// </summary>
NoAutocomplete = 1 << 16,
}
}
2 changes: 1 addition & 1 deletion BooruSharp/Booru/Template/BooruOnRails.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public abstract class BooruOnRails : ABooru
/// </param>
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)
| BooruOptions.NoLastComments | BooruOptions.NoWiki | BooruOptions.NoRelated | BooruOptions.NoAutocomplete)
{ }

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion BooruSharp/Booru/Template/Danbooru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public abstract class Danbooru : ABooru
/// </param>
protected Danbooru(string domain, BooruOptions options = BooruOptions.None)
: base(domain, UrlFormat.Danbooru, options | BooruOptions.NoLastComments | BooruOptions.NoPostCount
| BooruOptions.NoFavorite)
| BooruOptions.NoFavorite | BooruOptions.NoAutocomplete)
{ }

/// <inheritdoc/>
Expand Down
31 changes: 31 additions & 0 deletions BooruSharp/Booru/Template/E621.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,36 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso
}

// GetRelatedSearchResult not available // TODO: Available with credentials?

private protected override Search.Autocomplete.SearchResult[] GetAutocompleteResultAsync(object json)
{
var elem = (JArray)json;
var autoCompleteResults = new List<Search.Autocomplete.SearchResult>();
foreach (var item in elem.Children())
{
int id = item["id"].Value<int>();
string name = item["name"].Value<string>();
int count = item["post_count"].Value<int>();
var type = GetTagType(item["category"].Value<int>());
string antecedentName = item["antecedent_name"].Value<string>();
autoCompleteResults.Add(new Search.Autocomplete.SearchResult(id, name, name, type, count, antecedentName));
}
return autoCompleteResults.ToArray();
}

private Search.Tag.TagType GetTagType(int typeNum)
{
switch (typeNum)
{
case 5: return Search.Tag.TagType.Species;
case 8: return Search.Tag.TagType.Lore;
case 0: return Search.Tag.TagType.Trivia;
case 4: return Search.Tag.TagType.Character;
case 3: return Search.Tag.TagType.Copyright;
case 1: return Search.Tag.TagType.Artist;
case 7: return Search.Tag.TagType.Metadata;
default: return (Search.Tag.TagType)6; //i won't question...
}
}
}
}
46 changes: 46 additions & 0 deletions BooruSharp/Booru/Template/Gelbooru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
Expand Down Expand Up @@ -146,5 +147,50 @@ private protected override async Task<IEnumerable> GetTagEnumerableSearchResultA
}
throw new InvalidTags();
}

/// <summary>
/// Gets a result of autocomplete options from a tag slice
/// <para>(GelBooru variant)</para>
/// </summary>
/// <param name="query">The tag slice to autocomplete</param>
/// <returns>The task object representing the asynchronous operation.</returns>
/// <exception cref="System.Net.Http.HttpRequestException"/>
public override async Task<Search.Autocomplete.SearchResult[]> AutocompleteAsync(string query) //I commited to adding GelBooru one way or another, so here we are.
{
//No need to check for autocomplete API because this is an override.

Uri url = new Uri(BaseUrl + $"index.php?page=autocomplete2&term={query}&type=tag_query&limit=10");

var array = JsonConvert.DeserializeObject<JArray>(await GetJsonAsync(url));

return GetAutocompleteResultAsync(array);
}

private protected override Search.Autocomplete.SearchResult[] GetAutocompleteResultAsync(object json)
{
var elem = (JArray)json;
var autoCompleteResults = new List<Search.Autocomplete.SearchResult>();
foreach (var item in elem.Children())
{
string label = item["label"].Value<string>();
string name = item["value"].Value<string>();
int count = item["post_count"].Value<int>();
var type = GetTagType(item["category"].Value<string>());
autoCompleteResults.Add(new Search.Autocomplete.SearchResult(null, name, label, type, count, null));
}
return autoCompleteResults.ToArray();
}
private Search.Tag.TagType GetTagType(string typeName)
{
switch (typeName)
{
case "tag": return Search.Tag.TagType.Trivia;
case "character": return Search.Tag.TagType.Character;
case "copyright": return Search.Tag.TagType.Copyright;
case "artist": return Search.Tag.TagType.Artist;
case "metadata": return Search.Tag.TagType.Metadata;
default: return (Search.Tag.TagType)6; //i won't question...
}
}
}
}
16 changes: 16 additions & 0 deletions BooruSharp/Booru/Template/Gelbooru02.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Linq;
using System.Net.Http;
Expand Down Expand Up @@ -110,6 +112,20 @@ private protected override Search.Tag.SearchResult GetTagSearchResult(object jso

// GetRelatedSearchResult not available

private protected override Search.Autocomplete.SearchResult[] GetAutocompleteResultAsync(object json)
{
var elem = (JArray)json;
var autoCompleteResults = new List<Search.Autocomplete.SearchResult>();
foreach (var item in elem.Children())
{
string label = item["label"].Value<string>();
string name = item["value"].Value<string>();
int count = Convert.ToInt32(Regex.Match(label, @"\(([^()]*)\)$").Groups[1].Value); //this should always work
autoCompleteResults.Add(new Search.Autocomplete.SearchResult(null, name, label, null, count, null));
}
return autoCompleteResults.ToArray();
}

private readonly string _url;
}
}
2 changes: 1 addition & 1 deletion BooruSharp/Booru/Template/Moebooru.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public abstract class Moebooru : ABooru
/// </param>
protected Moebooru(string domain, BooruOptions options = BooruOptions.None)
: base(domain, UrlFormat.PostIndexJson, options | BooruOptions.NoPostByMD5
| BooruOptions.NoFavorite)
| BooruOptions.NoFavorite | BooruOptions.NoAutocomplete)
{ }

/// <inheritdoc/>
Expand Down
2 changes: 1 addition & 1 deletion BooruSharp/Booru/Template/Philomena.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public abstract class Philomena : ABooru
/// </param>
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)
| BooruOptions.NoLastComments | BooruOptions.NoWiki | BooruOptions.NoRelated | BooruOptions.NoAutocomplete)
{ }

/// <summary>
Expand Down
34 changes: 34 additions & 0 deletions BooruSharp/Search/Autocomplete/ABooru.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Threading.Tasks;

namespace BooruSharp.Booru
{
public abstract partial class ABooru
{
/// <summary>
/// Gets a result of autocomplete options from a tag slice
/// </summary>
/// <param name="query">The tag slice to autocomplete</param>
/// <returns>The task object representing the asynchronous operation.</returns>
/// <exception cref="Search.FeatureUnavailable"/>
/// <exception cref="System.Net.Http.HttpRequestException"/>
public virtual async Task<Search.Autocomplete.SearchResult[]> AutocompleteAsync(string query)
{
if (!HasAutocompleteAPI)
throw new Search.FeatureUnavailable();

if (query.Length < 3)
throw new ArgumentException("Autocomplete query must be longer than 3 characters");

Uri url = _format == UrlFormat.Danbooru
? CreateUrl(_autocompleteUrl, SearchArg("name_matches") + query)
: CreateUrl(_autocompleteUrl, SearchArg("q") + query);

var array = JsonConvert.DeserializeObject<JArray>(await GetJsonAsync(url));

return GetAutocompleteResultAsync(array);
}
}
}
63 changes: 63 additions & 0 deletions BooruSharp/Search/Autocomplete/SearchResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using BooruSharp.Search.Tag;

namespace BooruSharp.Search.Autocomplete
{
///<summary>
/// Represents an autocomplete API search result.
///</summary>
public readonly struct SearchResult
{
/// <summary>
/// Initializes a <see cref="SearchResult"/> struct.
/// </summary>
/// <param name="id">The ID of the tag.</param>
/// <param name="name">The name of the tag.</param>
/// <param name="label">The label (display name) of the tag.</param>
/// <param name="type">The type of the tag.</param>
/// <param name="count">The number of occurences of the tag.</param>
/// <param name="antecedent_name">The previous name of the tag.</param>
public SearchResult(int? id, string name, string label, TagType? type, int count, string antecedent_name)
{
TagID = id;
Label = label;
Name = name;
Type = type;
Count = count;
AntecedentName = antecedent_name;
}

/// <summary>
/// Gets the ID of the tag.
/// <see langword="null"/> if not on E621 or E926.
/// </summary>
public int? TagID { get; }

/// <summary>
/// Gets the label (display name) of the tag.
/// </summary>
public string Label { get; }

/// <summary>
/// Gets the name of the tag.
/// </summary>
public string Name { get; }

/// <summary>
/// Gets the type of the tag.
/// <see langword="null"/> if on GelBooru02
/// </summary>
/// Also known as "category".
public TagType? Type { get; }

/// <summary>
/// Gets the number of occurences of the tag.
/// </summary>
public int Count { get; }

/// <summary>
/// Gets the previous name of the tag.
/// <see langword="null"/> if not on E621 or E926.
/// </summary>
public string AntecedentName { get; }
}
}
46 changes: 26 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,26 @@ 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 | 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 | Autocomplete API |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Atfbooru | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ || ✔️ | ✔️ ||| ❌️ |
| Danbooru Donmai | ✔️ | ✔️ | ✔️ | ✔️ | ✔️ || ✔️ | ✔️ ||||
| Derpibooru | ✔️ | ✔️ ||| ✔️ |||| ✔️ |||
| E621 | ✔️ | ✔️ | ✔️ |||||||| ✔️ |
| E926 | ✔️ | ✔️ | ✔️ |||||||| ✔️ |
| 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
Expand Down Expand Up @@ -143,6 +143,12 @@ var booru = new BooruSharp.Booru.Safebooru();
booru.SetBooruAuth(new BooruSharp.Booru.BooruAuth("yourUserId", "yourPasswordHash")); // See https://github.com/Xwilarg/BooruSharp/#booru
await booru.AddFavoriteAsync(1759793);
```
### Get search autocomplete results
```Cs
var booru = new BooruSharp.Booru.Safebooru();
var results = await booru.AutocompleteAsync("kasuga_");
Console.WriteLine(String.Join(Environment.NewLine, results.Select(x => "Autocomplete result:" + x.Name)));
```
### Get all character tags containing a string
```Cs
var yandere = new BooruSharp.Booru.Yandere();
Expand Down

0 comments on commit f64dce0

Please sign in to comment.