diff --git a/database/init.sql b/database/init.sql index 93f4069db6b6aeb9cea0b9cc6ccf58ac22560cd0..c131ebded8cc8b632a818ec14ff01be6d4828fa1 100644 --- a/database/init.sql +++ b/database/init.sql @@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS language ); INSERT INTO language (name) VALUES - ('jp'), ('fr'), ('en'), ('ru'), ('sp'), ('it'), ('ch'); + ('jp'), ('fr'), ('en'), ('ru'), ('sp'), ('it'), ('ch'), ('multi'), ('undefined'); -- Playlists are sets of karas that are meant to be played together, kinda like -- M3U except it's stored in a SQL database. diff --git a/player/db/update.c b/player/db/update.c index db428e1b67a5541d80e05bcffdf533f049c18dc1..1cf6848d562cfb15692607b834265fa15900111d 100644 --- a/player/db/update.c +++ b/player/db/update.c @@ -11,238 +11,267 @@ static const char SQL_LAST_UPDATE[] = "SELECT last_update FROM misc;"; static const char SQL_INSERT_OLD_KARA[] = - "INSERT INTO " - "kara (song_name, source_name, category, language, file_path, is_new) " - "VALUES (?, ?, ?, ?, ?, 0)"; + "INSERT INTO " + "kara (song_name, source_name, category, language, file_path, is_new) " + "VALUES (?, ?, ?, ?, ?, 0)"; static const char SQL_INSERT_NEW_KARA[] = - "INSERT INTO " - "kara (song_name, source_name, category, language, file_path," - " is_new, author_name) " - "VALUES (?, ?, ?, ?, ?, 1, ?)"; + "INSERT INTO " + "kara (song_name, source_name, category, language, file_path," + " is_new, author_name) " + "VALUES (?, ?, ?, ?, ?, 1, ?)"; -static inline void * -mallocf(size_t n) +static inline void * mallocf(size_t n) { - void *res = malloc(n); - if (!res) - fprintf(stderr, "Failed to allocate %ld bytes.\n", n); - return res; -} + void * res = malloc(n); -static inline void * -reallocf(void *ptr, size_t n) -{ - void *res = realloc(ptr, n); - if (!res) - fprintf(stderr, "Failed to allocate %ld bytes.\n", n); - return res; -} + if (!res) + { fprintf(stderr, "Failed to allocate %ld bytes.\n", n); } -static int -directories_and_files(const struct dirent *entry) -{ - return (entry->d_type == DT_REG || entry->d_type == DT_DIR) && - entry->d_name[0] != '.'; + return res; } -static int -update_legacy_file(sqlite3 *db, const char *filename, size_t prefix, time_t last_update) +static inline void * reallocf(void * ptr, size_t n) { - struct stat attrs; - if (stat(filename, &attrs) < 0) - return -1; - if (attrs.st_mtim.tv_sec < last_update) - return 0; - - const char *pseudo = 0, - *category = 0, - *name = 0, - *type = 0, - *title = 0; - size_t pseudo_len = 0, - category_len = 0, - name_len = 0, - type_len = 0, - title_len = 0; - - const char *file_path = filename + prefix; - const char *f = file_path; - category_len = strcspn(f, "/"); - if (!strncmp(f, "nouveau", category_len)) { - // "Nouveau" kara, in nouveaux/{pseudo}/{category}/... - f += category_len + 1; // Skip "nouveaux/" - pseudo_len = strcspn(f, "/"); - pseudo = f; - f += pseudo_len + 1; // Skip "{pseudo}/" - category_len = strcspn(f, "/"); - } - - category = f; - f += category_len + 1; // Skip "{category}/" - name_len = strcspn(f, "-"); - name = f; - f += name_len + 1; - type_len = strcspn(f, "-"); - type = f; - f += type_len + 1; - title_len = strcspn(f, "."); - title = f; - - if (!title) { - fprintf(stderr, "Bad file path: '%s'.\n", filename); - return -1; - } - - int status_code = -1; - sqlite3_stmt *stmt = 0; - const char *sql = pseudo ? SQL_INSERT_NEW_KARA : SQL_INSERT_OLD_KARA; - - if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) { - serror(db, "Failed to prepare statement"); - goto error; - } - - if (sqlite3_bind_text(stmt, 0, name, name_len, 0) != SQLITE_OK) { - serror(db, "Failed to bind song_name"); - goto error; - } - - if (sqlite3_bind_text(stmt, 0, title, title_len, 0) != SQLITE_OK) { - serror(db, "Failed to bind song_source"); - goto error; - } + void * res = realloc(ptr, n); - if (sqlite3_bind_text(stmt, 0, category, -1, 0) != SQLITE_OK) { - serror(db, "Failed to bind category"); - goto error; - } - - if (sqlite3_bind_text(stmt, 0, type, -1, 0) != SQLITE_OK) { - serror(db, "Failed to bind type"); - goto error; - } - - if (sqlite3_bind_text(stmt, 0, file_path, -1, 0) != SQLITE_OK) { - serror(db, "Failed to bind file_path"); - goto error; - } + if (!res) + { fprintf(stderr, "Failed to allocate %ld bytes.\n", n); } - if (sqlite3_step(stmt) != SQLITE_DONE) { - // TODO handle SQLITE_BUSY (should rollback) - serror(db, "Failed to execute insert statement"); - goto error; - } - - status_code = 0; -error: - sqlite3_finalize(stmt); - return status_code; + return res; } -static int -update_directory(sqlite3 *db, const char *root, time_t last_update) +static int directories_and_files(const struct dirent * entry) { - int status_code = -1; - const size_t root_len = strlen(root) + 1; - char **queue = 0; - size_t queue_max = 128; - size_t queue_len = 0; - - queue = (char**) mallocf(queue_max * sizeof(char *)); - if (!queue) - goto error; - - queue[0] = strdup(root); - queue_len++; - - while (queue_len) { - struct dirent **children; - int n; - char *dir = queue[--queue_len]; - - n = scandir(dir, &children, directories_and_files, alphasort); - if (n < 0) - fprintf(stderr, "Failed to open '%s': %s.\n", dir, strerror(errno)); - - for (int i = 0; i < n; i++) { - size_t dir_len = strlen(dir); - - // child length (256) + null byte + slash - char *child = (char*) mallocf(dir_len + 258); - if (!child) - goto error; - - memcpy(child, dir, dir_len); - child[dir_len] = '/'; - strcpy(child + dir_len + 1, children[i]->d_name); - - if (children[i]->d_type == DT_REG) { - if (update_legacy_file(db, child, root_len, last_update) < 0) - fprintf(stderr, "Failed to add '%s'.\n", child); - free(child); - } else if (queue_len == queue_max) { - queue_max *= 2; - char **new_queue = (char**) reallocf(queue, queue_max * sizeof(char *)); - if (!new_queue) - goto error; - queue = new_queue; - queue[queue_len++] = child; - } else { - queue[queue_len++] = child; - } - } - } + return (entry->d_type == DT_REG || entry->d_type == DT_DIR) && + entry->d_name[0] != '.'; +} - status_code = 0; +static int update_legacy_file(sqlite3 * db, const char * filename, size_t prefix, time_t last_update) +{ + struct stat attrs; + + if (stat(filename, &attrs) < 0) + { return -1; } + + if (attrs.st_mtim.tv_sec < last_update) + { return 0; } + + const char * pseudo = 0; + const char * category = 0; + const char * name = 0; + const char * type = 0; + const char * title = 0; + size_t pseudo_len = 0, + category_len = 0, + name_len = 0, + type_len = 0, + title_len = 0; + const char * file_path = filename + prefix; + const char * f = file_path; + category_len = strcspn(f, "/"); + + if (!strncmp(f, "nouveau", category_len)) + { + // "Nouveau" kara, in nouveaux/{pseudo}/{category}/... + f += category_len + 1; // Skip "nouveaux/" + pseudo_len = strcspn(f, "/"); + pseudo = f; + f += pseudo_len + 1; // Skip "{pseudo}/" + category_len = strcspn(f, "/"); + } + + category = f; + f += category_len + 1; // Skip "{category}/" + name_len = strcspn(f, "-"); + name = f; + f += name_len + 1; + type_len = strcspn(f, "-"); + type = f; + f += type_len + 1; + title_len = strcspn(f, "."); + title = f; + + if (!title) + { + fprintf(stderr, "Bad file path: '%s'.\n", filename); + return -1; + } + + int status_code = -1; + sqlite3_stmt * stmt = 0; + const char * sql = pseudo ? SQL_INSERT_NEW_KARA : SQL_INSERT_OLD_KARA; + + if (sqlite3_prepare_v2(db, sql, -1, &stmt, 0) != SQLITE_OK) + { + serror(db, "Failed to prepare statement"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, name, name_len, 0) != SQLITE_OK) + { + serror(db, "Failed to bind song_name"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, title, title_len, 0) != SQLITE_OK) + { + serror(db, "Failed to bind song_source"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, category, -1, 0) != SQLITE_OK) + { + serror(db, "Failed to bind category"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, type, -1, 0) != SQLITE_OK) + { + serror(db, "Failed to bind type"); + goto error; + } + + if (sqlite3_bind_text(stmt, 0, file_path, -1, 0) != SQLITE_OK) + { + serror(db, "Failed to bind file_path"); + goto error; + } + + status_code = sqlite3_step(stmt); + if (status_code != SQLITE_DONE) + { + // TODO handle SQLITE_BUSY (should rollback) + serror(db, "Failed to execute insert statement"); + goto error; + } + + status_code = 0; error: - for (size_t i = 0; i < queue_len; i++) - free(queue[i]); - free(queue); - return status_code; + sqlite3_finalize(stmt); + return status_code; } -int -lektor_db_update(sqlite3 *db, const char *directory) +static int update_directory(sqlite3 * db, const char * root, time_t last_update) { - if (sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0) != SQLITE_OK) { - serror(db, "Failed to start transaction"); - return -1; + int status_code = -1; + const size_t root_len = strlen(root) + 1; + char ** queue = 0; + size_t queue_max = 128; + size_t queue_len = 0; + queue = (char **) mallocf(queue_max * sizeof(char *)); + + if (!queue) + { goto error; } + + queue[0] = strdup(root); + queue_len++; + + while (queue_len) + { + struct dirent ** children; + int n; + char * dir = queue[--queue_len]; + n = scandir(dir, &children, directories_and_files, alphasort); + + if (n < 0) + { fprintf(stderr, "Failed to open '%s': %s.\n", dir, strerror(errno)); } + + for (int i = 0; i < n; i++) + { + size_t dir_len = strlen(dir); + // child length (256) + null byte + slash + char * child = (char *) mallocf(dir_len + 258); + + if (!child) + { goto error; } + + memcpy(child, dir, dir_len); + child[dir_len] = '/'; + strcpy(child + dir_len + 1, children[i]->d_name); + + if (children[i]->d_type == DT_REG) + { + if (update_legacy_file(db, child, root_len, last_update) < 0) + { fprintf(stderr, "Failed to add '%s'.\n", child); } + + free(child); + } + else if (queue_len == queue_max) + { + queue_max *= 2; + char ** new_queue = (char **) reallocf(queue, queue_max * sizeof(char *)); + + if (!new_queue) + { goto error; } + + queue = new_queue; + queue[queue_len++] = child; + } + else + { + queue[queue_len++] = child; + } } + } - sqlite3_stmt *stmt = 0; - time_t last_update = 0; - - if (sqlite3_prepare_v2(db, SQL_LAST_UPDATE, -1, &stmt, 0) != SQLITE_OK) { - serror(db, "Failed to get last update time"); - goto error; - } - switch (sqlite3_step(stmt)) { - case SQLITE_ROW: - last_update = (time_t) sqlite3_column_int(stmt, 0); - break; - case SQLITE_DONE: - fprintf(stderr, "Failed to get last update time: table misc is empty"); - goto error; - default: - serror(db, "Failed to get last update time"); - goto error; - } + status_code = 0; +error: - if (update_directory(db, directory, last_update) < 0) - goto error; + for (size_t i = 0; i < queue_len; i++) + { free(queue[i]); } - if (sqlite3_exec(db, "COMMIT;", 0, 0, 0) != SQLITE_OK) { - serror(db, "Failed to commit transaction"); - goto error; - } - - sqlite3_finalize(stmt); - return 0; + free(queue); + return status_code; +} +int lektor_db_update(sqlite3 * db, const char * directory) +{ + if (sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0) != SQLITE_OK) + { + serror(db, "Failed to start transaction"); + return -1; + } + + sqlite3_stmt * stmt = 0; + time_t last_update = 0; + + if (sqlite3_prepare_v2(db, SQL_LAST_UPDATE, -1, &stmt, 0) != SQLITE_OK) + { + serror(db, "Failed to get last update time"); + goto error; + } + + switch (sqlite3_step(stmt)) + { + case SQLITE_ROW: + last_update = (time_t) sqlite3_column_int(stmt, 0); + break; + + case SQLITE_DONE: + fprintf(stderr, "Failed to get last update time: table misc is empty"); + goto error; + + default: + serror(db, "Failed to get last update time"); + goto error; + } + + if (update_directory(db, directory, last_update) < 0) + { goto error; } + + if (sqlite3_exec(db, "COMMIT;", 0, 0, 0) != SQLITE_OK) + { + serror(db, "Failed to commit transaction"); + goto error; + } + + sqlite3_finalize(stmt); + return 0; error: + sqlite3_finalize(stmt); - sqlite3_finalize(stmt); - if (sqlite3_exec(db, "ROLLBACK;", 0, 0, 0) != SQLITE_OK) - serror(db, "Failed to rollback transaction, database is corrupted"); - return -1; + if (sqlite3_exec(db, "ROLLBACK;", 0, 0, 0) != SQLITE_OK) + { serror(db, "Failed to rollback transaction, database is corrupted"); } + + return -1; } diff --git a/player/db/utils.c b/player/db/utils.c index 0683fd22b0483b31b677577f84d50687613f0240..83f04df2b1d16ae4032be421e7047ebdeec435c1 100644 --- a/player/db/utils.c +++ b/player/db/utils.c @@ -1,7 +1,6 @@ #include "utils.h" -void -serror(sqlite3 *db, const char *msg) +void serror(sqlite3 * db, const char * msg) { - fprintf(stderr, "%s: %s\n", msg, sqlite3_errmsg(db)); + fprintf(stderr, "%s: %s\n", msg, sqlite3_errmsg(db)); }