diff --git a/inc/lektor/database.h b/inc/lektor/database.h index 38349136e3b336a338748d9dcb583f4ec6335430..e46f067b94e4a4fc8a12b1a0e956f4056373b24c 100644 --- a/inc/lektor/database.h +++ b/inc/lektor/database.h @@ -43,6 +43,7 @@ bool database_sync_mpv_state(sqlite3 *db, mpv_handle **mpv); /* Update the database. */ bool database_update(sqlite3 *db, const char *kara_dir); bool database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail); +bool database_update_set_available(sqlite3 *db, uint64_t id); /* Control the content of the queue. */ bool database_queue_add_id (sqlite3 *db, int id, int priority); diff --git a/inc/lektor/net.h b/inc/lektor/net.h index ac9c15065ec93514f88aad02022ba2bd156491be..0c1428a1b7bf2b83d6b5218b6a0b8d7cef3f0d7d 100644 --- a/inc/lektor/net.h +++ b/inc/lektor/net.h @@ -38,7 +38,7 @@ struct lkt_repo { volatile sqlite3 *db; }; -int repo_new(struct lkt_repo *const repo, const char *name, const char *url, volatile sqlite3 *db); +int repo_new (struct lkt_repo *const repo, const char *name, const char *url, volatile sqlite3 *db); void repo_free(struct lkt_repo *const repo); /* Get metadata of a kara. */ @@ -61,6 +61,7 @@ struct lkt_command lkt_command_parse(char *raw); struct lkt_command lkt_command_clone(struct lkt_command *c); void lkt_command_free(struct lkt_command *c); +/* Messages that are send and recieved */ struct lkt_message { size_t ref_count; char data[LKT_MESSAGE_MAX]; @@ -94,7 +95,7 @@ void lkt_state_send(struct lkt_state *srv, size_t c, struct lkt_message *msg); /* Get and set continuation state for a client. */ size_t lkt_remaining_msg(struct lkt_state *srv, size_t c); -int lkt_get_continuation(struct lkt_state *srv, size_t c); +int lkt_get_continuation(struct lkt_state *srv, size_t c); void lkt_set_continuation(struct lkt_state *srv, size_t c, int i); /* Get the mask to watch for events for a given client, to be used with the `idle` command. diff --git a/src/database/update.c b/src/database/update.c index 2f1f89aacec1a9e65c43ce4114be2b28e0196fc4..5262aed5fded505ec54b1c32fca1142970059a29 100644 --- a/src/database/update.c +++ b/src/database/update.c @@ -57,6 +57,24 @@ error: return status; } +bool +database_update_set_available(sqlite3 *db, uint64_t id) +{ + static const char *SQL_STMT = "UPDATE kara SET available = 1 WHERE id = ?;"; + sqlite3_stmt *stmt; + SQLITE_PREPARE(db, stmt, SQL_STMT, error); + SQLITE_BIND_INT(db, stmt, 1, (int) id, error); + if (sqlite3_step(stmt) != SQLITE_OK) + goto error; + sqlite3_finalize(stmt); + LOG_INFO_SCT("DB", "Kara %ld is now available", id); + return true; +error: + sqlite3_finalize(stmt); + LOG_ERROR_SCT("DB", "Failed to set kara %ld available", id); + return false; +} + bool database_update_add(sqlite3 *db, const char *kara_path, struct kara_metadata *mdt, uint64_t id, bool avail) { diff --git a/src/net/downloader.c b/src/net/downloader.c index b3dd76027e3a689014194f1c980ea3b6078286af..7ff999453e2318d265e5334f0171617fa02b05e7 100644 --- a/src/net/downloader.c +++ b/src/net/downloader.c @@ -153,10 +153,9 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata CURLcode res; struct json_object *jobj, *field; int ret = 1, err = 0; + char url[URL_MAX_LEN]; RETURN_UNLESS(mdt, "Invalid argument", 1); - char *url = calloc(URL_MAX_LEN, sizeof(char)); - RETURN_UNLESS(url, "Out of memory", errno = ENOMEM); struct memory file = { .mem = NULL, @@ -171,11 +170,9 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_mem__); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - res = curl_easy_perform(curl_handle); - if (res != CURLE_OK) { + if (CURLE_OK != (res = curl_easy_perform(curl_handle))) { LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(res)); - free(url); free(file.mem); curl_easy_cleanup(curl_handle); return 1; @@ -223,82 +220,96 @@ repo_get_id(struct lkt_repo *const repo, const uint64_t id, struct kara_metadata ret = 0; err: - free(url); json_object_put(jobj); /* Delete object. */ return ret; } -int -repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt_ret) +static inline int +__download_kara(const char *url, const char *path, int override) { - RETURN_UNLESS(kara_path, "Invalid argument", 1); - struct kara_metadata mdt; CURL *curl_handle; - int ret = 1, fd; - char *url = calloc(URL_MAX_LEN, sizeof(char)); - char *ct; - RETURN_UNLESS(url, "Out of memory", errno = ENOMEM); - - if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) { - LOG_ERROR("%s", "Failed to get kara metadata from kurisu"); - goto err_no_curl; - } - errno = 0; - fd = open(kara_path, - O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW, - S_IRUSR | S_IWUSR); + char *ct, ret = 1; + int fd = open(path, O_WRONLY | O_APPEND | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR); +retest: if (fd < 0) { - if (errno == EEXIST) - LOG_ERROR("File '%s' already exists", kara_path); + if (errno == EEXIST && ! override) + LOG_ERROR("File '%s' already exists", path); + + else if (errno == EEXIST && override) { + if (unlink(path)) { + LOG_ERROR_SCT("REPO", "Failed to unlink file '%s'", path); + return 1; + } + + fd = open(path, O_WRONLY | O_CREAT | O_NOFOLLOW, S_IRUSR | S_IWUSR); + override = false; + goto retest; + } else - LOG_ERROR("Could not open file '%s'", kara_path); + LOG_ERROR("Could not open file '%s'", path); - goto err_no_curl; + return 1; } struct file file = { - .path = kara_path, + .path = path, .fd = fd, }; - memset(url, 0, URL_MAX_LEN * sizeof(char)); - snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id); - url[URL_MAX_LEN - 1] = 0; curl_handle = curl_easy_init(); curl_easy_setopt(curl_handle, CURLOPT_URL, url); curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_disk__); curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &file); curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0"); - ret = curl_easy_perform(curl_handle); - if (ret != CURLE_OK) { + if (CURLE_OK != (ret = curl_easy_perform(curl_handle))) { LOG_ERROR_SCT("CURL", "curl_easy_perform failed: %s", curl_easy_strerror(ret)); goto err; } - if (CURLE_OK == (ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct))) - LOG_INFO_SCT("CURL", "Content-Type is '%s'", ct); - else { + if (CURLE_OK != (ret = curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_TYPE, &ct))) { LOG_ERROR_SCT("CURL", "Failed to get Content-Type: %s", curl_easy_strerror(ret)); goto err; } - if (repo->db && !database_update_add((sqlite3 *) repo->db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) { - LOG_ERROR("%s", "Failed to add kara to database"); - goto err; - } - ret = 0; err: curl_easy_cleanup(curl_handle); -err_no_curl: - free(url); return ret; } +int +repo_download_id_sync(struct lkt_repo *const repo, const uint64_t id, const char *kara_path, struct kara_metadata *mdt_ret) +{ + RETURN_UNLESS(kara_path, "Invalid argument", 1); + struct kara_metadata mdt; + char url[URL_MAX_LEN]; + + if (repo_get_id(repo, id, mdt_ret ? mdt_ret : &mdt)) { + LOG_ERROR("%s", "Failed to get kara metadata from kurisu"); + return 1; + } + + memset(url, 0, URL_MAX_LEN * sizeof(char)); + snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, id); + url[URL_MAX_LEN - 1] = 0; + + if (__download_kara(url, kara_path, false)) { + LOG_ERROR_SCT("REPO", "Failed to download kara '%s' with url '%s'", kara_path, url); + return 1; + } + + if (repo->db && !database_update_add((sqlite3 *) repo->db, kara_path, mdt_ret ? mdt_ret : &mdt, id, true)) { + LOG_ERROR("%s", "Failed to add kara to database"); + return 1; + } + + return 0; +} + /* Download a kara */ static inline void * @@ -341,9 +352,9 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec { size_t i, len = json_object_array_length(json); struct json_object *kara_json; - int32_t integer; + int32_t integer, err; struct kara *kara; - int err; + char url[URL_MAX_LEN]; RETURN_UNLESS(len > 0 && json_object_get_array(json), "Json invalid or array empty", NOTHING); LOG_INFO_SCT("REPO", "Starting to process json for repo %s", repo->name); @@ -355,7 +366,7 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec /* Get the id of the kara. */ if (safe_json_get_int32(kara_json, "id", &integer)) - goto err; + continue; kara->id = integer; /* Craft a fake filepath here, it will be used later. */ @@ -380,8 +391,24 @@ __handle_got_json(volatile sqlite3 *db, struct lkt_repo *repo, struct json_objec if (safe_json_get_int32(kara_json, "song_number", &kara->mdt.song_number)) goto err; - if (database_update_add((sqlite3 *) db, kara->filename, &kara->mdt, kara->id, false)) + if (database_update_add((sqlite3 *) db, kara->filename, &kara->mdt, kara->id, false)) { LOG_ERROR_SCT("REPO", "Could not add unavailable kara %ld to db", kara->id); + continue; + } + + snprintf(url, URL_MAX_LEN - 1, repo->get_id_file, kara->id); + url[URL_MAX_LEN - 1] = '\0'; + + if (__download_kara(url, kara->filename, true)) { + LOG_WARN_SCT("REPO", "Could not download kara %ld at path '%s'", kara->id, kara->filename); + continue; + } + + if (database_update_set_available((sqlite3 *) db, kara->id)) { + LOG_WARN_SCT("REPO", "Could not set kara %ld available", kara->id); + continue; + } + LOG_INFO_SCT("REPO", "Added kara %ld from repo %s, filepath is %s", kara->id, repo->name, kara->filename); }