From be61d8dabc5610307e84237ccd6007e65878559f Mon Sep 17 00:00:00 2001 From: x3 Date: Wed, 9 Aug 2023 17:26:53 +0200 Subject: [PATCH] Add 'myliststats' command --- src/api.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++------ src/api.h | 12 +++++++ src/cmd.c | 2 ++ src/cmd.h | 5 +++ src/cmd_stats.c | 40 ++++++++++++++++++++++ src/config.c | 6 ++++ src/net.c | 4 +-- src/net.h | 5 ++- 8 files changed, 161 insertions(+), 17 deletions(-) create mode 100644 src/cmd_stats.c diff --git a/src/api.c b/src/api.c index 1532c29..25c61e7 100644 --- a/src/api.c +++ b/src/api.c @@ -484,27 +484,34 @@ static enum error api_ratelimit() /* * Returns the written byte count - * Or -1 on error, and -2 if errno was EINTR + * Or negative errno value on error */ static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size) { + pthread_mutex_lock(&api_work_mx); + ssize_t read_len; int en; - if (api_ratelimit() == ERR_SHOULD_EXIT) - return -2; + if (api_ratelimit() == ERR_SHOULD_EXIT) { + read_len = -2; + goto end; + } uio_debug("{Api}: Sending: %.*s", (int)data_len, buffer); if (api_encryption) data_len = api_encrypt(buffer, data_len); en = net_send(buffer, data_len); - if (en < 0) - return en; + if (en < 0) { + read_len = en; + goto end; + } read_len = net_read(buffer, buf_size); if (read_len < 0) { - uio_error("!!! BAD PLACE EINTR !!! report pls"); - return read_len; /* This could lead so some problems if we also want to + if (read_len == -EINTR) + uio_error("!!! BAD PLACE EINTR !!! report pls"); + goto end; /* This could lead so some problems if we also want to log out. If we hit this, the msg got sent, but we couldn't read the response. That means, in the logout call, this msg's data will be read @@ -516,6 +523,8 @@ static ssize_t api_send(char *buffer, size_t data_len, size_t buf_size) read_len = api_decrypt(buffer, read_len); uio_debug("{Api}: Reading: %.*s", (int)read_len, buffer); +end: + pthread_mutex_unlock(&api_work_mx); return read_len; } @@ -686,7 +695,6 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, enum error err = NOERR; int retry_count = 0; - pthread_mutex_lock(&api_work_mx); api_g_retry_count = 0; while (retry_count < API_MAX_TRYAGAIN && @@ -701,7 +709,7 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, res_len = api_send(buffer, send_len, API_BUFSIZE); if (res_len < 0) { - if (res_len == -2 && should_exit) + if (res_len == -EINTR && should_exit) err = ERR_SHOULD_EXIT; else err = ERR_API_COMMFAIL; @@ -742,10 +750,24 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, } if (err == ERR_API_NOLOGIN || err == ERR_API_INV_SESSION) { + if (api_authed == false) { + /* If this happens and we are not logged in, don't retry */ + err = ERR_API_COMMFAIL; + break; + } api_g_retry_count++; if (api_g_retry_count < API_MAX_TRYAGAIN) { - uio_debug("Let's try loggin in agane"); + struct timespec ts; + MS_TO_TIMESPEC_L(ts, 30000); + + uio_debug("Let's try loggin in agane after waiting some"); api_authed = false; /* We got logged out probably */ + if (nanosleep(&ts, NULL) == -1) { + if (errno == EINTR && should_exit) { + err = ERR_SHOULD_EXIT; + goto end; + } + } err = api_auth(); /* -> will call this function */ if (api_g_retry_count < API_MAX_TRYAGAIN) { memcpy(buffer, l_buff, send_len); @@ -764,7 +786,6 @@ static enum error api_cmd_base_pref(char buffer[API_BUFSIZE], int send_len, } end: - pthread_mutex_unlock(&api_work_mx); return err; } static enum error api_cmd_base(char buffer[API_BUFSIZE], struct api_result *res, @@ -1022,4 +1043,65 @@ enum error api_cmd_mylistmod(uint64_t lid, struct api_mylistadd_opts *opts, return err; } +static enum error api_cmd_myliststats_resp_parse(const char *buffer, struct api_myliststats_result *stats) +{ + /* all int + * + * {animes}|{eps}|{files}|{size of files}|{added animes}|{added eps}| + * {added files}|{added groups}|{leech %}|{glory %}|{viewed % of db}| + * {mylist % of db}|{viewed % of mylist}|{number of viewed eps}| + * {votes}|{reviews}|{viewed length in minutes} + */ + + bool glr; + char *line; + size_t line_len; + int fc; + + glr = api_get_line(buffer, 2, &line, &line_len); + if (!glr) + return ERR_API_RESP_INVALID; + fc = api_field_parse(line, + "%lu", &stats->animes, + "%lu", &stats->eps, + "%lu", &stats->files, + "%lu", &stats->size_of_files, + "%lu", &stats->added_animes, + "%lu", &stats->added_eps, + "%lu", &stats->added_files, + "%lu", &stats->added_groups, + "%lu", &stats->leech_prcnt, + "%lu", &stats->glory_prcnt, + "%lu", &stats->viewed_prcnt_of_db, + "%lu", &stats->mylist_prcnt_of_db, + "%lu", &stats->viewed_prcnt_of_mylist, + "%lu", &stats->num_of_viewed_eps, + "%lu", &stats->votes, + "%lu", &stats->reviews, + "%lu", &stats->viewed_minutes, + NULL); + if (fc != 17) { + uio_error("Scanf only parsed %d", fc); + return ERR_API_RESP_INVALID; + } + + return NOERR; +} + +enum error api_cmd_myliststats(struct api_result *res) +{ + char buffer[API_BUFSIZE]; + enum error err = NOERR; + + err = api_cmd_base(buffer, res, "MYLISTSTATS s=%s", api_session); + //err = api_cmd_base(buffer, res, "MYLISTSTATS"); + if (err != NOERR) + return err; + + if (res->code == APICODE_MYLIST_STATS) + return api_cmd_myliststats_resp_parse(buffer, &res->myliststats); + else + return ERR_API_RESP_INVALID; +} + #pragma GCC diagnostic pop diff --git a/src/api.h b/src/api.h index e7f43e5..90fd5dc 100644 --- a/src/api.h +++ b/src/api.h @@ -2,6 +2,7 @@ #define _API_H #include #include +#include #include "error.h" @@ -243,6 +244,15 @@ struct api_mylistadd_result { struct api_mylistmod_result { uint32_t n_edits; }; +/* The largest struct probably lul 136 bytes */ +struct api_myliststats_result { + uint64_t animes, eps, files, size_of_files; + uint64_t added_animes, added_eps, added_files; + uint64_t added_groups, leech_prcnt, glory_prcnt; + uint64_t viewed_prcnt_of_db, mylist_prcnt_of_db; + uint64_t viewed_prcnt_of_mylist, num_of_viewed_eps; + uint64_t votes, reviews, viewed_minutes; +}; #define e(n) struct api_##n##_result n struct api_result { @@ -255,6 +265,7 @@ struct api_result { e(mylistadd); e(encrypt); e(mylistmod); + e(myliststats); }; }; #undef e @@ -269,5 +280,6 @@ enum error api_cmd_mylistadd(int64_t size, const uint8_t *hash, struct api_mylistadd_opts *opts, struct api_result *res); enum error api_cmd_mylistmod(uint64_t lid, struct api_mylistadd_opts *opts, struct api_result *res); +enum error api_cmd_myliststats(struct api_result *res); #endif /* _API_H */ diff --git a/src/cmd.c b/src/cmd.c index 8e5e62f..5244ab8 100644 --- a/src/cmd.c +++ b/src/cmd.c @@ -23,8 +23,10 @@ static const struct cmd_entry ents[] = { { .arg_name = "server-version", .fn = cmd_server_version, .need_api = true }, { .arg_name = "uptime", .fn = cmd_server_uptime, .need_auth = true }, { .arg_name = "ed2k", .fn = cmd_ed2k, }, + { .arg_name = "add", .fn = cmd_add, .argcheck = cmd_add_argcheck, .need_auth = true, .need_cache = true, }, { .arg_name = "modify", .fn = cmd_modify, .argcheck = cmd_modify_argcheck, .need_auth = true, .need_cache = true, }, + { .arg_name = "stats", .fn = cmd_stats, .need_auth = true, .need_cache = false, }, }; static const int32_t ents_len = sizeof(ents)/sizeof(*ents); diff --git a/src/cmd.h b/src/cmd.h index 709bb5c..f541b99 100644 --- a/src/cmd.h +++ b/src/cmd.h @@ -42,4 +42,9 @@ enum error cmd_prog_version(void *); enum error cmd_modify(void *data); enum error cmd_modify_argcheck(); +/* + * Request the mylist stats + */ +enum error cmd_stats(void *data); + #endif /* _CMD_H */ diff --git a/src/cmd_stats.c b/src/cmd_stats.c new file mode 100644 index 0000000..ef8a149 --- /dev/null +++ b/src/cmd_stats.c @@ -0,0 +1,40 @@ +#include + +#include "error.h" +#include "api.h" +#include "util.h" +#include "uio.h" + +enum error cmd_stats(void *data) +{ + enum error err = NOERR; + struct api_result res; + struct api_myliststats_result *stats = &res.myliststats; + + err = api_cmd_myliststats(&res); + if (err != NOERR) { + uio_error("Failed to request stats from API server"); + return err; + } + + printf("Mylist status:\n" + "Anime: %lu\n" + "Episodes: %lu\n" + "Files: %lu\n" + "Size of files: %lu\n" + "Added groups: %lu\n" + "leech%%: %lu\n" + "Glory%%: %lu\n" + "Viewed%%/DB: %lu\n" + "Mylist%%/DB: %lu\n" + "Viewed%%/Mylist: %lu\n" + "Viewed eps: %lu\n" + "Votes: %lu\n" + "Reviews: %lu\n" + "Viewed minutes: %lu\n", + stats->animes, stats->eps, stats->files, stats->size_of_files, stats->added_groups, + stats->leech_prcnt, stats->glory_prcnt, stats->viewed_prcnt_of_db, + stats->mylist_prcnt_of_db, stats->viewed_prcnt_of_mylist, stats->num_of_viewed_eps, + stats->votes, stats->reviews, stats->viewed_minutes); + return NOERR; +} diff --git a/src/config.c b/src/config.c index 0690eb3..afd381c 100644 --- a/src/config.c +++ b/src/config.c @@ -171,6 +171,12 @@ static struct conf_entry options[] = { .subopts_count = sizeof(modify_add_subopts) / sizeof(modify_add_subopts[0]), }, + { .l_name = "stats", .s_name = UCHAR_MAX + 10, + .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, + .type = OTYPE_SUBCOMMAND, .handle_order = 1, + .h_desc = "Get the mylist status", + }, + /*{ .l_name = "stats", .s_name = UCHAR_MAX + 5, .has_arg = no_argument, .set_func = config_set_bool, .in_args = true, .type = OTYPE_B, .handle_order = 1, .value_is_set = true },*/ diff --git a/src/net.c b/src/net.c index 7c6ac2b..55a6858 100644 --- a/src/net.c +++ b/src/net.c @@ -265,9 +265,7 @@ ssize_t net_read(void* out_data, size_t read_size) int en = errno; uio_error("{net} Read failed: %s", strerror(errno)); - if (en == EINTR) - return -2; - return -1; + return -en; } if (read == read_size) uio_warning("{net} Data may have been discarded!"); diff --git a/src/net.h b/src/net.h index 347b59c..5f003d0 100644 --- a/src/net.h +++ b/src/net.h @@ -12,9 +12,8 @@ enum error net_init(); /* * Send and read data to and from the api - * Returns the number of bytes sent/read or -1 on error - * If the error is EINTR, it returns -2 - */ + * Returns the number of bytes sent/read or + * a negative value which is the errno */ ssize_t net_send(const void *msg, size_t msg_len); ssize_t net_read(void *out_data, size_t read_size);