nekos-best.c 1.0.0
A simple, lightweight c wrapper for nekos.best API
nekosbest.h
Go to the documentation of this file.
1/// \file nekosbest.h This file contains structs and functions for interacting with the nekos.best API.
2
3#ifndef NEKOSBEST_H
4#define NEKOSBEST_H
5
6#ifdef __cplusplus
7extern "C" {
8#endif
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <curl/curl.h>
14#include <cjson/cJSON.h>
15
16/// Base URL for nekos.best API.
17#define NEKOS_BASE_URL "https://nekos.best/api/v2/"
18
19/// Maximum amount of results that can be requested.
20#define NEKOS_MAX_AMOUNT 20
21
22/// Minimum length of query string.
23#define NEKOS_MIN_QUERY_LEN 3
24/// Maximum length of query string.
25#define NEKOS_MAX_QUERY_LEN 150
26
27/// Status codes for the nekos.best c wrapper.
28typedef enum {
29 NEKOS_OK, ///< Indicates that the operation was successful.
30 NEKOS_MEM_ERR, ///< Indicates that there was a memory allocation error.
31 NEKOS_LIBCURL_ERR, ///< Indicates that there was an error with libcurl.
32 NEKOS_CJSON_ERR, ///< Indicates that there was an error with cJSON.
33 NEKOS_INVALID_PARAM_ERR ///< Indicates that an invalid parameter was passed to a function.
35
36/// Enum for the format of the image.
37typedef enum {
38 NEKOS_PNG, ///< Indicates that the response image is a png and \link nekos_source_png nekos_source_png \endlink should be used.
39 NEKOS_GIF ///< Indicates that the response image is a gif and \link nekos_source_gif nekos_source_gif \endlink should be used.
41
42/// Struct for an endpoint/category.
43typedef struct {
44 char *name; ///< [out] Name of the endpoint/category.
45 nekos_format format; ///< [out] Format of the endpoint/category.
47
48/// Struct for a list of endpoints/categories.
49typedef struct {
50 nekos_endpoint *endpoints; ///< [out] Array of endpoints/categories.
51 size_t len; ///< [out] Amount of endpoints/categories.
53
54/// Struct for a gif source.
55typedef struct {
56 char *anime_name; ///< [out] Name of the anime the gif is from.
58
59/// Struct for a png source.
60typedef struct {
61 char *artist_name; ///< [out] Name of the artist of the png.
62 char *artist_href; ///< [out] URL to the artist's page.
63 char *source_url; ///< [out] URL to the source of the png.
65
66/// Struct for a result image.
67typedef struct {
68 nekos_format format; ///< [out] Format of the image.
69 union {
70 nekos_source_gif *gif; ///< [out] Source information for a gif (only use if the format is \link NEKOS_GIF nekos_format::NEKOS_GIF \endlink).
71 nekos_source_png *png; ///< [out] Source information for a png (only use if the format is \link NEKOS_PNG nekos_format::NEKOS_PNG \endlink).
72 } source; ///< [out] Source information of the image.
73 char* url; ///< [out] URL to the image.
75
76/// Struct for a list of result images.
77typedef struct {
78 nekos_result *responses; ///< [out] Array of result images.
79 size_t len; ///< [out] Amount of result images.
81
82/**
83 * Struct for an http response.
84 */
85typedef struct {
86 char *text; ///< [out] Non-nullterminated text of the response.
87 size_t len; ///< [out] Length of the response text.
89
90#ifndef NEKOSBEST_IMPL
91
92/**
93 * Get a list of endpoints/categories.
94 *
95 * This function fetches the `endpoints` endpoint of the api
96 * and parses the response into a list of endpoints.
97 *
98 * It will allocate memory for the list of endpoints and the endpoints themselves.
99 *
100 * \param [out] endpoints
101 * Pointer to a \link nekos_endpoint_list nekos_endpoint_list \endlink to store the endpoints in.
102 *
103 * \return
104 * ::NEKOS_OK \n
105 * ::NEKOS_MEM_ERR \n
106 * ::NEKOS_LIBCURL_ERR \n
107 * ::NEKOS_CJSON_ERR
108 */
110
111/**
112 * Find a specific endpoint in a list of endpoints/categories.
113 *
114 * This function searches for a specific endpoint in a list of endpoints by name.
115 *
116 * \param [in] endpoints
117 * Pointer to a \link nekos_endpoint_list nekos_endpoint_list \endlink to search in.
118 * \param [in] name
119 * Name of the endpoint to search for.
120 *
121 * \return
122 * Pointer to the endpoint if found, NULL otherwise.
123 */
124nekos_endpoint* nekos_find_endpoint(const nekos_endpoint_list* endpoints, const char* name);
125
126/**
127 * Get a list of images from a category.
128 *
129 * This function fetches the specified category endpoint of the api
130 * and parses the response into a list of images.
131 *
132 * It will allocate memory for the list of results, the results themselves, and the source information.
133 *
134 * \param [out] results
135 * Pointer to a \link nekos_result_list nekos_result_list \endlink to store the results in.
136 * \param [in] endpoint
137 * Pointer to a \link nekos_endpoint nekos_endpoint \endlink to specify the category.
138 * \param [in] amount
139 * Amount of images to fetch. Must be between 1 and \link NEKOS_MAX_AMOUNT \endlink.
140 *
141 * \return
142 * ::NEKOS_OK \n
143 * ::NEKOS_MEM_ERR \n
144 * ::NEKOS_LIBCURL_ERR \n
145 * ::NEKOS_CJSON_ERR \n
146 * ::NEKOS_INVALID_PARAM_ERR
147 */
148nekos_status nekos_category(nekos_result_list *results, const nekos_endpoint *endpoint, int amount);
149
150/**
151 * Search for images.
152 *
153 * This function fetches the `search` endpoint of the api
154 * and parses the response into a list of images.
155 *
156 * The search can optionally specify a endpoint/category.
157 *
158 * It will allocate memory for the list of results, the results themselves, and the source information.
159 *
160 * \param [out] results
161 * Pointer to a \link nekos_result_list nekos_result_list \endlink to store the results in.
162 * \param [in] raw_query
163 * Query to search for. Must be between \link NEKOS_MIN_QUERY_LEN \endlink and \link NEKOS_MAX_QUERY_LEN \endlink.
164 * \param [in] amount
165 * Amount of images to fetch. Must be between 1 and \link NEKOS_MAX_AMOUNT \endlink.
166 * \param [in] format
167 * Format of the images to search for.
168 * \param [in] endpoint
169 * Pointer to a \link nekos_endpoint nekos_endpoint \endlink to specify a category. Can be NULL.
170 *
171 * \return
172 * ::NEKOS_OK \n
173 * ::NEKOS_MEM_ERR \n
174 * ::NEKOS_LIBCURL_ERR \n
175 * ::NEKOS_CJSON_ERR \n
176 * ::NEKOS_INVALID_PARAM_ERR
177 */
178nekos_status nekos_search(nekos_result_list *results, const char* raw_query, int amount, const nekos_format format, const nekos_endpoint *endpoint);
179
180/**
181 * Download an image.
182 *
183 * This function fetches the specified image url
184 * and stores the response in a \link nekos_http_response nekos_http_response \endlink.
185 *
186 * It will allocate memory for the response text.
187 *
188 * \param [out] http_response
189 * Pointer to a \link nekos_http_response nekos_http_response \endlink to store the response in.
190 * \param [in] url
191 * URL of the image to download.
192 *
193 * \return
194 * ::NEKOS_OK \n
195 * ::NEKOS_MEM_ERR \n
196 * ::NEKOS_LIBCURL_ERR
197 */
198nekos_status nekos_download(nekos_http_response *http_response, const char* url);
199
200/**
201 * Free an endpoint.
202 *
203 * This function frees the memory allocated for the endpoint.
204 *
205 * \param [in] endpoint
206 * Pointer to a \link nekos_endpoint nekos_endpoint \endlink to free.
207*/
209
210/**
211 * Free a list of endpoints.
212 *
213 * This function frees the memory allocated for the list of endpoints and the endpoints themselves.
214 *
215 * \param [in] endpoints
216 * Pointer to a \link nekos_endpoint_list nekos_endpoint_list \endlink to free.
217 */
219
220/**
221 * Free a result.
222 *
223 * This function frees the memory allocated for the result and the source information.
224 *
225 * \param [in] result
226 * Pointer to a \link nekos_result nekos_result \endlink to free.
227 */
228void nekos_free_result(const nekos_result* result);
229
230/**
231 * Free a list of results.
232 *
233 * This function frees the memory allocated for the list of results, the results themselves, and the source information.
234 *
235 * \param [in] results
236 * Pointer to a \link nekos_result_list nekos_result_list \endlink to free.
237 */
239
240/**
241 * Free an http response.
242 *
243 * This function frees the memory allocated for the response text.
244 *
245 * \param [in] http_response
246 * Pointer to a \link nekos_http_response nekos_http_response \endlink to free.
247 */
249
250#else // NEKOSBEST_IMPL
251
252static size_t nekos_write_callback(const void *ptr, size_t count, size_t nmemb, nekos_http_response *http_response) {
253 size_t size = count * nmemb;
254 size_t new_len = http_response->len + size;
255
256 // resize response text
257 char* new_text = (char*) realloc(http_response->text, new_len + 1);
258 if (!new_text) {
259 free(http_response->text);
260 return CURLE_ABORTED_BY_CALLBACK;
261 }
262
263 // copy new data to response text
264 memcpy(new_text + http_response->len, ptr, size);
265 http_response->text = new_text;
266 http_response->len = new_len;
267 return size;
268}
269
270static nekos_status nekos_do_request(nekos_http_response *http_response, const char* url) {
271 // initialize http response object
272 http_response->len = 0;
273 http_response->text = (char*) malloc(1);
274 if (!http_response->text)
275 return NEKOS_MEM_ERR;
276
277 // initialize curl
278 CURL *curl = curl_easy_init();
279 if (!curl)
280 return NEKOS_LIBCURL_ERR;
281
282 // configure curl request
283 curl_easy_setopt(curl, CURLOPT_URL, url);
284 curl_easy_setopt(curl, CURLOPT_CA_CACHE_TIMEOUT, 604800L);
285 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, nekos_write_callback);
286 curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_response);
287
288 // make request
289 CURLcode res = curl_easy_perform(curl);
290 if (res != CURLE_OK)
291 return NEKOS_LIBCURL_ERR;
292
293 // cleanup curl
294 curl_easy_cleanup(curl);
295 return NEKOS_OK;
296}
297
298static char* nekos_jsondup(const cJSON *json, const char* key) {
299 const cJSON *element = cJSON_GetObjectItemCaseSensitive(json, key);
300 char* str = (char*) malloc(strlen(element->valuestring) + 1);
301 strcpy(str, element->valuestring);
302 return str;
303}
304
306 // make request
307 nekos_http_response http_response;
308 nekos_status http_status = nekos_do_request(&http_response, NEKOS_BASE_URL "endpoints");
309 if (http_status != NEKOS_OK)
310 return http_status;
311
312 // parse response
313 cJSON *json = cJSON_ParseWithLength(http_response.text, http_response.len);
314 if (!json || !cJSON_IsObject(json))
315 return NEKOS_CJSON_ERR;
316
317 // parse json
318 endpoints->len = cJSON_GetArraySize(json);
319 endpoints->endpoints = (nekos_endpoint*) malloc(endpoints->len * sizeof(nekos_endpoint));
320
321 // iterate through endpoints
322 const cJSON *endpoint_obj;
323 size_t i = 0;
324 cJSON_ArrayForEach(endpoint_obj, json) {
325 char* name = endpoint_obj->string;
326 endpoints->endpoints[i].name = (char*) malloc(strlen(name) + 1);
327 strcpy(endpoints->endpoints[i].name, name);
328
329 const cJSON *format_obj = cJSON_GetObjectItemCaseSensitive(endpoint_obj, "format");
330 endpoints->endpoints[i].format = strcmp(format_obj->valuestring, "png") == 0 ? NEKOS_PNG : NEKOS_GIF;
331
332 i++;
333 }
334
335 // cleanup
336 cJSON_Delete(json);
337 free(http_response.text);
338 return NEKOS_OK;
339}
340
341nekos_endpoint* nekos_find_endpoint(const nekos_endpoint_list* endpoints, const char* name) {
342 for (size_t i = 0; i < endpoints->len; i++) {
343 if (strcmp(endpoints->endpoints[i].name, name) == 0)
344 return &endpoints->endpoints[i];
345 }
346
347 return NULL;
348}
349
350nekos_status nekos_category(nekos_result_list *results, const nekos_endpoint *endpoint, int amount) {
351 // check if amount is valid
352 if (amount < 1 || amount > NEKOS_MAX_AMOUNT)
354
355 // create endpoint url
356 char url[96];
357 snprintf(url, 96, NEKOS_BASE_URL "%s?amount=%d", endpoint->name, amount);
358
359 // make request
360 nekos_http_response http_response;
361 nekos_status http_status = nekos_do_request(&http_response, url);
362 if (http_status != NEKOS_OK)
363 return http_status;
364
365 // parse response
366 cJSON *json = cJSON_ParseWithLength(http_response.text, http_response.len);
367 if (!json || !cJSON_IsObject(json))
368 return NEKOS_CJSON_ERR;
369
370 // parse json
371 cJSON *results_obj = cJSON_GetObjectItemCaseSensitive(json, "results");
372 results->len = cJSON_GetArraySize(results_obj);
373 results->responses = (nekos_result*) malloc(results->len * sizeof(nekos_result));
374
375 // iterate through responses
376 const cJSON *response_obj;
377 size_t i = 0;
378 cJSON_ArrayForEach(response_obj, results_obj) {
379 results->responses[i].url = nekos_jsondup(response_obj, "url");
380 results->responses[i].format = endpoint->format;
381 if (endpoint->format == NEKOS_GIF) {
382 results->responses[i].source.gif = (nekos_source_gif*) malloc(sizeof(nekos_source_gif));
383 results->responses[i].source.gif->anime_name = nekos_jsondup(response_obj, "anime_name");
384 } else {
385 results->responses[i].source.png = (nekos_source_png*) malloc(sizeof(nekos_source_png));
386 results->responses[i].source.png->artist_name = nekos_jsondup(response_obj, "artist_name");
387 results->responses[i].source.png->artist_href = nekos_jsondup(response_obj, "artist_href");
388 results->responses[i].source.png->source_url = nekos_jsondup(response_obj, "source_url");
389 }
390 i++;
391 }
392
393 // cleanup
394 cJSON_Delete(json);
395 free(http_response.text);
396 return NEKOS_OK;
397}
398
399nekos_status nekos_search(nekos_result_list *results, const char* raw_query, int amount, const nekos_format format, const nekos_endpoint *endpoint) {
400 // check if amount is valid
401 if (amount < 1 || amount > NEKOS_MAX_AMOUNT)
403
404 // url encode query
405 char* query = curl_easy_escape(NULL, raw_query, 0);
406
407 // check if query is valid
408 size_t query_len = strlen(query);
409 if (query_len < NEKOS_MIN_QUERY_LEN || query_len > NEKOS_MAX_QUERY_LEN)
411
412 // create endpoint url
413 char url[256];
414 if (endpoint)
415 snprintf(url, 256, NEKOS_BASE_URL "search?query=%s&type=%d&amount=%d&category=%s", query, format + 1, amount, endpoint->name);
416 else
417 snprintf(url, 256, NEKOS_BASE_URL "search?query=%s&type=%d&amount=%d", query, format + 1, amount);
418
419 // make request
420 nekos_http_response http_response;
421 nekos_status http_status = nekos_do_request(&http_response, url);
422 if (http_status != NEKOS_OK)
423 return http_status;
424
425 // parse response
426 cJSON *json = cJSON_ParseWithLength(http_response.text, http_response.len);
427 if (!json || !cJSON_IsObject(json))
428 return NEKOS_CJSON_ERR;
429
430 // parse json
431 cJSON *results_obj = cJSON_GetObjectItemCaseSensitive(json, "results");
432 results->len = cJSON_GetArraySize(results_obj);
433 results->responses = (nekos_result*) malloc(results->len * sizeof(nekos_result));
434
435 // iterate through responses
436 const cJSON *response_obj;
437 size_t i = 0;
438 cJSON_ArrayForEach(response_obj, results_obj) {
439 results->responses[i].url = nekos_jsondup(response_obj, "url");
440 results->responses[i].format = format;
441 if (format == NEKOS_GIF) {
442 results->responses[i].source.gif = (nekos_source_gif*) malloc(sizeof(nekos_source_gif));
443 results->responses[i].source.gif->anime_name = nekos_jsondup(response_obj, "anime_name");
444 } else {
445 results->responses[i].source.png = (nekos_source_png*) malloc(sizeof(nekos_source_png));
446 results->responses[i].source.png->artist_name = nekos_jsondup(response_obj, "artist_name");
447 results->responses[i].source.png->artist_href = nekos_jsondup(response_obj, "artist_href");
448 results->responses[i].source.png->source_url = nekos_jsondup(response_obj, "source_url");
449 }
450
451 i++;
452 }
453
454 // cleanup
455 cJSON_Delete(json);
456 free(http_response.text);
457 curl_free(query);
458 return NEKOS_OK;
459}
460
461nekos_status nekos_download(nekos_http_response *http_response, const char* url) {
462 return nekos_do_request(http_response, url);
463}
464
465void nekos_free_endpoint(const nekos_endpoint* endpoint) {
466 free(endpoint->name);
467}
468
469void nekos_free_endpoints(const nekos_endpoint_list* endpoints) {
470 for (size_t i = 0; i < endpoints->len; i++)
471 nekos_free_endpoint(&endpoints->endpoints[i]);
472
473 free(endpoints->endpoints);
474}
475
476void nekos_free_result(const nekos_result* result) {
477 free(result->url);
478 if (result->format == NEKOS_GIF) {
479 free(result->source.gif->anime_name);
480 free(result->source.gif);
481 } else {
482 free(result->source.png->artist_name);
483 free(result->source.png->artist_href);
484 free(result->source.png->source_url);
485 free(result->source.png);
486 }
487}
488
489void nekos_free_results(const nekos_result_list* results) {
490 for (size_t i = 0; i < results->len; i++)
491 nekos_free_result(&results->responses[i]);
492
493 free(results->responses);
494}
495
496void nekos_free_http_response(const nekos_http_response* http_response) {
497 free(http_response->text);
498}
499
500#endif // NEKOSBEST_IMPL
501
502#ifdef __cplusplus
503}
504#endif
505
506#endif // NEKOSBEST_H
nekos_status nekos_search(nekos_result_list *results, const char *raw_query, int amount, const nekos_format format, const nekos_endpoint *endpoint)
Search for images.
#define NEKOS_MAX_QUERY_LEN
Maximum length of query string.
Definition nekosbest.h:25
nekos_format
Enum for the format of the image.
Definition nekosbest.h:37
@ NEKOS_GIF
Indicates that the response image is a gif and nekos_source_gif should be used.
Definition nekosbest.h:39
@ NEKOS_PNG
Indicates that the response image is a png and nekos_source_png should be used.
Definition nekosbest.h:38
nekos_status
Status codes for the nekos.best c wrapper.
Definition nekosbest.h:28
@ NEKOS_OK
Indicates that the operation was successful.
Definition nekosbest.h:29
@ NEKOS_LIBCURL_ERR
Indicates that there was an error with libcurl.
Definition nekosbest.h:31
@ NEKOS_CJSON_ERR
Indicates that there was an error with cJSON.
Definition nekosbest.h:32
@ NEKOS_INVALID_PARAM_ERR
Indicates that an invalid parameter was passed to a function.
Definition nekosbest.h:33
@ NEKOS_MEM_ERR
Indicates that there was a memory allocation error.
Definition nekosbest.h:30
#define NEKOS_BASE_URL
Base URL for nekos.best API.
Definition nekosbest.h:17
nekos_status nekos_download(nekos_http_response *http_response, const char *url)
Download an image.
#define NEKOS_MAX_AMOUNT
Maximum amount of results that can be requested.
Definition nekosbest.h:20
void nekos_free_result(const nekos_result *result)
Free a result.
nekos_endpoint * nekos_find_endpoint(const nekos_endpoint_list *endpoints, const char *name)
Find a specific endpoint in a list of endpoints/categories.
void nekos_free_http_response(const nekos_http_response *http_response)
Free an http response.
void nekos_free_endpoints(const nekos_endpoint_list *endpoints)
Free a list of endpoints.
void nekos_free_results(const nekos_result_list *results)
Free a list of results.
nekos_status nekos_category(nekos_result_list *results, const nekos_endpoint *endpoint, int amount)
Get a list of images from a category.
nekos_status nekos_endpoints(nekos_endpoint_list *endpoints)
Get a list of endpoints/categories.
void nekos_free_endpoint(const nekos_endpoint *endpoint)
Free an endpoint.
Struct for a list of endpoints/categories.
Definition nekosbest.h:49
nekos_endpoint * endpoints
[out] Array of endpoints/categories.
Definition nekosbest.h:50
size_t len
[out] Amount of endpoints/categories.
Definition nekosbest.h:51
Struct for an endpoint/category.
Definition nekosbest.h:43
char * name
[out] Name of the endpoint/category.
Definition nekosbest.h:44
nekos_format format
[out] Format of the endpoint/category.
Definition nekosbest.h:45
Struct for an http response.
Definition nekosbest.h:85
char * text
[out] Non-nullterminated text of the response.
Definition nekosbest.h:86
size_t len
[out] Length of the response text.
Definition nekosbest.h:87
Struct for a list of result images.
Definition nekosbest.h:77
nekos_result * responses
[out] Array of result images.
Definition nekosbest.h:78
size_t len
[out] Amount of result images.
Definition nekosbest.h:79
Struct for a result image.
Definition nekosbest.h:67
union nekos_result::@0 source
[out] Source information of the image.
nekos_source_gif * gif
[out] Source information for a gif (only use if the format is nekos_format::NEKOS_GIF ).
Definition nekosbest.h:70
nekos_source_png * png
[out] Source information for a png (only use if the format is nekos_format::NEKOS_PNG ).
Definition nekosbest.h:71
char * url
[out] URL to the image.
Definition nekosbest.h:73
nekos_format format
[out] Format of the image.
Definition nekosbest.h:68
Struct for a gif source.
Definition nekosbest.h:55
char * anime_name
[out] Name of the anime the gif is from.
Definition nekosbest.h:56
Struct for a png source.
Definition nekosbest.h:60
char * artist_href
[out] URL to the artist's page.
Definition nekosbest.h:62
char * artist_name
[out] Name of the artist of the png.
Definition nekosbest.h:61
char * source_url
[out] URL to the source of the png.
Definition nekosbest.h:63